Merge "Expose InputConsumer::probablyHasInput" into main
diff --git a/Android.bp b/Android.bp
index 7f1ef67..8c4dfbb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,6 +97,12 @@
],
}
+aidl_library {
+ name: "PersistableBundle_aidl",
+ hdrs: ["aidl/binder/android/os/PersistableBundle.aidl"],
+ strip_import_prefix: "aidl/binder",
+}
+
cc_library_headers {
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl
index 493ecb4..248e973 100644
--- a/aidl/binder/android/os/PersistableBundle.aidl
+++ b/aidl/binder/android/os/PersistableBundle.aidl
@@ -17,4 +17,4 @@
package android.os;
-@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h" ndk_header "android/persistable_bundle_aidl.h";
diff --git a/cmds/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..0bbd4a8 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>
@@ -177,6 +178,7 @@
#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
#define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat"
+#define KERNEL_CONFIG "/proc/config.gz"
#define WLUTIL "/vendor/xbin/wlutil"
#define WMTRACE_DATA_DIR "/data/misc/wmtrace"
#define OTA_METADATA_DIR "/metadata/ota"
@@ -244,7 +246,7 @@
static const std::string DUMP_HALS_TASK = "DUMP HALS";
static const std::string DUMP_BOARD_TASK = "dumpstate_board()";
static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS";
-static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES";
+static const std::string SERIALIZE_PERFETTO_TRACE_TASK = "SERIALIZE PERFETTO TRACE";
namespace android {
namespace os {
@@ -1034,8 +1036,6 @@
CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
DoRadioLogcat();
- RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
-
/* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable",
"-v", "uid", "-d", "*:v"});
@@ -1086,10 +1086,16 @@
static void MaybeAddSystemTraceToZip() {
// This function copies into the .zip the system trace that was snapshotted
- // by the early call to MaybeSnapshotSystemTrace(), if any background
+ // by the early call to MaybeSnapshotSystemTraceAsync(), 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 MaybeSnapshotSystemTraceAsync() 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(
@@ -1236,7 +1242,7 @@
static void DumpIpAddrAndRules() {
/* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
- RunCommand("NETWORK INTERFACES", {"ip", "link"});
+ RunCommand("NETWORK INTERFACES", {"ip", "-s", "link"});
RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
RunCommand("IP RULES", {"ip", "rule", "show"});
@@ -1519,7 +1525,7 @@
}
static void DumpstateLimitedOnly() {
- // Trimmed-down version of dumpstate to only include a whitelisted
+ // Trimmed-down version of dumpstate to only include a allowlisted
// set of logs (system log, event log, and system server / system app
// crashes, and networking logs). See b/136273873 and b/138459828
// for context.
@@ -1635,7 +1641,7 @@
// Enqueue slow functions into the thread pool, if the parallel run is enabled.
std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins,
- dump_netstats_report, post_process_ui_traces;
+ dump_netstats_report;
if (ds.dump_pool_) {
// Pool was shutdown in DumpstateDefaultAfterCritical method in order to
// drop root user. Restarts it.
@@ -1649,8 +1655,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 +1864,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;
@@ -1954,6 +1952,8 @@
DumpFile("PSI memory", "/proc/pressure/memory");
DumpFile("PSI io", "/proc/pressure/io");
+ ds.AddZipEntry(ZIP_ROOT_DIR + KERNEL_CONFIG, KERNEL_CONFIG);
+
RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"},
CommandOptions::WithTimeout(10).Always().DropRoot().Build());
@@ -2977,14 +2977,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 +3022,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,7 +3077,9 @@
}
void Dumpstate::PreDumpUiData() {
+ auto snapshot_system_trace = MaybeSnapshotSystemTraceAsync();
MaybeSnapshotUiTraces();
+ MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace));
}
/*
@@ -3257,25 +3265,26 @@
// 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();
+ std::future<std::string> snapshot_system_trace;
+ 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();
+ snapshot_system_trace = MaybeSnapshotSystemTraceAsync();
+
+ // 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();
+ MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace));
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
if (options_->telephony_only) {
@@ -3371,24 +3380,59 @@
TakeScreenshot();
}
-void Dumpstate::MaybeSnapshotSystemTrace() {
- // 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)
- // case that no trace is ongoing, this command is a no-op.
- // 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;
- // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
- // file in the later stages.
+std::future<std::string> Dumpstate::MaybeSnapshotSystemTraceAsync() {
+ // 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.
+ // This function must run asynchronously to avoid delaying MaybeTakeEarlyScreenshot() in the
+ // standard invocation states (b/316110955).
+ if (options_->use_predumped_ui_data) {
+ return {};
+ }
+
+ // Create temporary file for the command's output
+ std::string outPath = ds.bugreport_internal_dir_ + "/tmp_serialize_perfetto_trace";
+ auto outFd = android::base::unique_fd(TEMP_FAILURE_RETRY(
+ open(outPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ if (outFd < 0) {
+ MYLOGE("Could not open %s to serialize perfetto trace.\n", outPath.c_str());
+ return {};
+ }
+
+ // If a stale file exists already, remove it.
+ unlink(SYSTEM_TRACE_SNAPSHOT);
+
+ MYLOGI("Launching async '%s'", SERIALIZE_PERFETTO_TRACE_TASK.c_str())
+ return std::async(
+ std::launch::async, [this, outPath = std::move(outPath), outFd = std::move(outFd)] {
+ // 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)
+ // case that no trace is ongoing, this command is a no-op.
+ // 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.
+ RunCommand(
+ SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"},
+ CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build(),
+ false, outFd);
+ // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
+ // file in the later stages.
+
+ return outPath;
+ });
+}
+
+void Dumpstate::MaybeWaitForSnapshotSystemTrace(std::future<std::string> task) {
+ if (!task.valid()) {
+ return;
+ }
+
+ WaitForTask(std::move(task), SERIALIZE_PERFETTO_TRACE_TASK, STDOUT_FILENO);
}
void Dumpstate::MaybeSnapshotUiTraces() {
@@ -3413,33 +3457,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..20b2865 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,15 +560,16 @@
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();
void MaybeTakeEarlyScreenshot();
- void MaybeSnapshotSystemTrace();
+ std::future<std::string> MaybeSnapshotSystemTraceAsync();
+ void MaybeWaitForSnapshotSystemTrace(std::future<std::string> task);
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..c30c00f
--- /dev/null
+++ b/cmds/evemu-record/main.rs
@@ -0,0 +1,213 @@
+/*
+ * 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, ValueEnum};
+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>,
+
+ /// The base time that timestamps should be relative to (Android-specific extension)
+ #[arg(long, value_enum, default_value_t = TimestampBase::FirstEvent)]
+ timestamp_base: TimestampBase,
+}
+
+#[derive(Clone, Debug, ValueEnum)]
+enum TimestampBase {
+ /// The first event received from the device.
+ FirstEvent,
+
+ /// The time when the system booted.
+ Boot,
+}
+
+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,
+ timestamp_base: TimestampBase,
+) -> 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()?;
+ let start_time = match timestamp_base {
+ // 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
+ TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1),
+ TimestampBase::Boot => TimeVal::new(0, 0),
+ };
+ 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, args.timestamp_base)?;
+ 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..c8ab8c0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -40,6 +40,7 @@
#include <fstream>
#include <functional>
#include <regex>
+#include <thread>
#include <unordered_set>
#include <android-base/file.h>
@@ -69,7 +70,6 @@
#include "installd_deps.h"
#include "otapreopt_utils.h"
#include "utils.h"
-#include "view_compiler.h"
#include "CacheTracker.h"
#include "CrateManager.h"
@@ -472,6 +472,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 +523,70 @@
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)) {
- ScopedTrace tracer("label-change");
+ if (inProgress || before != after) {
if (existing) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
- << path << "; running recursive restorecon";
+ << path << "; running recursive restorecon";
}
- 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;
+
+ auto restorecon = [path, seInfo, uid]() {
+ ScopedTrace tracer("label-change");
+
+ // 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;
+ return -1;
+ }
+ return 0;
+ };
+ if (inProgress) {
+ // The previous restorecon was interrupted. It's either crashed (unlikely), or the phone
+ // was rebooted. Possibly because it took too much time. This time let's move it to a
+ // separate thread - so it won't block the rest of the OS.
+ std::thread(restorecon).detach();
+ } else {
+ if (int result = restorecon(); result) {
+ return result;
+ }
}
}
- 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 +3315,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 +3341,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/OWNERS b/cmds/installd/OWNERS
index 643b2c2..e9fb85b 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -1,11 +1,10 @@
set noparent
-calin@google.com
jsharkey@android.com
maco@google.com
mast@google.com
+jiakaiz@google.com
narayan@google.com
ngeoffray@google.com
rpl@google.com
-toddke@google.com
patb@google.com
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/cmds/ip-up-vpn/Android.bp b/cmds/ip-up-vpn/Android.bp
deleted file mode 100644
index c746f7f..0000000
--- a/cmds/ip-up-vpn/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2011 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
- name: "ip-up-vpn",
-
- srcs: ["ip-up-vpn.c"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: [
- "libcutils",
- "liblog",
- ],
-}
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
deleted file mode 100644
index 71f0837..0000000
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2011 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 "ip-up-vpn"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <linux/if.h>
-#include <linux/route.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <log/log.h>
-
-#define DIR "/data/misc/vpn/"
-
-static const char *env(const char *name) {
- const char *value = getenv(name);
- return value ? value : "";
-}
-
-static int set_address(struct sockaddr *sa, const char *address) {
- sa->sa_family = AF_INET;
- errno = EINVAL;
- return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr);
-}
-
-/*
- * The primary goal is to create a file with VPN parameters. Currently they
- * are interface, addresses, routes, DNS servers, and search domains and VPN
- * server address. Each parameter occupies one line in the file, and it can be
- * an empty string or space-separated values. The order and the format must be
- * consistent with com.android.server.connectivity.Vpn. Here is an example.
- *
- * ppp0
- * 192.168.1.100/24
- * 0.0.0.0/0
- * 192.168.1.1 192.168.1.2
- * example.org
- * 192.0.2.1
- *
- * The secondary goal is to unify the outcome of VPN. The current baseline
- * is to have an interface configured with the given address and netmask
- * and maybe add a host route to protect the tunnel. PPP-based VPN already
- * does this, but others might not. Routes, DNS servers, and search domains
- * are handled by the framework since they can be overridden by the users.
- */
-int main(int argc, char **argv)
-{
- FILE *state = fopen(DIR ".tmp", "wb");
- if (!state) {
- ALOGE("Cannot create state: %s", strerror(errno));
- return 1;
- }
-
- if (argc >= 6) {
- /* Invoked by pppd. */
- fprintf(state, "%s\n", argv[1]);
- fprintf(state, "%s/32\n", argv[4]);
- fprintf(state, "0.0.0.0/0\n");
- fprintf(state, "%s %s\n", env("DNS1"), env("DNS2"));
- fprintf(state, "\n");
- fprintf(state, "\n");
- } else if (argc == 2) {
- /* Invoked by racoon. */
- const char *interface = env("INTERFACE");
- const char *address = env("INTERNAL_ADDR4");
- const char *routes = env("SPLIT_INCLUDE_CIDR");
-
- int s = socket(AF_INET, SOCK_DGRAM, 0);
- struct ifreq ifr;
- memset(&ifr, 0, sizeof(ifr));
-
- /* Bring up the interface. */
- ifr.ifr_flags = IFF_UP;
- strncpy(ifr.ifr_name, interface, IFNAMSIZ);
- if (ioctl(s, SIOCSIFFLAGS, &ifr)) {
- ALOGE("Cannot bring up %s: %s", interface, strerror(errno));
- fclose(state);
- return 1;
- }
-
- /* Set the address. */
- if (!set_address(&ifr.ifr_addr, address) ||
- ioctl(s, SIOCSIFADDR, &ifr)) {
- ALOGE("Cannot set address: %s", strerror(errno));
- fclose(state);
- return 1;
- }
-
- /* Set the netmask. */
- if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) {
- if (ioctl(s, SIOCSIFNETMASK, &ifr)) {
- ALOGE("Cannot set netmask: %s", strerror(errno));
- fclose(state);
- return 1;
- }
- }
-
- /* TODO: Send few packets to trigger phase 2? */
-
- fprintf(state, "%s\n", interface);
- fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4"));
- fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0");
- fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST"));
- fprintf(state, "%s\n", env("DEFAULT_DOMAIN"));
- fprintf(state, "%s\n", env("REMOTE_ADDR"));
- } else {
- ALOGE("Cannot parse parameters");
- fclose(state);
- return 1;
- }
-
- fclose(state);
- if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) {
- ALOGE("Cannot write state: %s", strerror(errno));
- return 1;
- }
- return 0;
-}
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index e54f9d3..870e8eb 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -44,6 +44,7 @@
#include "Timeout.h"
#include "utils.h"
+using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hidl::base::V1_0::DebugInfo;
@@ -522,27 +523,35 @@
using namespace ::android::hardware;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
- using std::literals::chrono_literals::operator""s;
- auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
- std::map<std::string, TableEntry> entries;
- for (const auto &info : infos) {
- std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
- std::string{info.instanceName.c_str()};
- entries.emplace(interfaceName, TableEntry{
- .interfaceName = interfaceName,
- .transport = vintf::Transport::PASSTHROUGH,
- .clientPids = info.clientPids,
- }).first->second.arch |= fromBaseArchitecture(info.arch);
- }
- for (auto &&pair : entries) {
- putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second));
- }
- });
+
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if ret.isOk(), the background thread has
+ // already ended, so it is safe to dereference entries.
+ auto entries = std::make_shared<std::map<std::string, TableEntry>>();
+ auto ret =
+ timeoutIPC(mLshal.getDebugDumpWait(), manager, &IServiceManager::debugDump,
+ [entries](const auto& infos) {
+ for (const auto& info : infos) {
+ std::string interfaceName = std::string{info.interfaceName.c_str()} +
+ "/" + std::string{info.instanceName.c_str()};
+ entries->emplace(interfaceName,
+ TableEntry{
+ .interfaceName = interfaceName,
+ .transport = vintf::Transport::PASSTHROUGH,
+ .clientPids = info.clientPids,
+ })
+ .first->second.arch |= fromBaseArchitecture(info.arch);
+ }
+ });
if (!ret.isOk()) {
err() << "Error: Failed to call list on getPassthroughServiceManager(): "
<< ret.description() << std::endl;
return DUMP_ALL_LIBS_ERROR;
}
+ for (auto&& pair : *entries) {
+ putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second));
+ }
return OK;
}
@@ -553,27 +562,40 @@
using namespace ::android::hardware::details;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
- auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
- for (const auto &info : infos) {
- if (info.clientPids.size() <= 0) {
- continue;
- }
- putEntry(HalType::PASSTHROUGH_CLIENTS, {
- .interfaceName =
- std::string{info.interfaceName.c_str()} + "/" +
- std::string{info.instanceName.c_str()},
- .transport = vintf::Transport::PASSTHROUGH,
- .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
- .clientPids = info.clientPids,
- .arch = fromBaseArchitecture(info.arch)
- });
- }
- });
+
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if ret.isOk(), the background thread has
+ // already ended, so it is safe to dereference entries.
+ auto entries = std::make_shared<std::vector<TableEntry>>();
+ auto ret =
+ timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::debugDump,
+ [entries](const auto& infos) {
+ for (const auto& info : infos) {
+ if (info.clientPids.size() <= 0) {
+ continue;
+ }
+ entries->emplace_back(
+ TableEntry{.interfaceName =
+ std::string{info.interfaceName.c_str()} +
+ "/" +
+ std::string{info.instanceName.c_str()},
+ .transport = vintf::Transport::PASSTHROUGH,
+ .serverPid = info.clientPids.size() == 1
+ ? info.clientPids[0]
+ : NO_PID,
+ .clientPids = info.clientPids,
+ .arch = fromBaseArchitecture(info.arch)});
+ }
+ });
if (!ret.isOk()) {
err() << "Error: Failed to call debugDump on defaultServiceManager(): "
<< ret.description() << std::endl;
return DUMP_PASSTHROUGH_ERROR;
}
+ for (auto&& entry : *entries) {
+ putEntry(HalType::PASSTHROUGH_CLIENTS, std::move(entry));
+ }
return OK;
}
@@ -583,11 +605,14 @@
if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; }
const vintf::Transport mode = vintf::Transport::HWBINDER;
- hidl_vec<hidl_string> fqInstanceNames;
- // copying out for timeoutIPC
- auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
- fqInstanceNames = names;
- });
+
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if listRet.isOk(), the background thread has
+ // already ended, so it is safe to dereference fqInstanceNames.
+ auto fqInstanceNames = std::make_shared<hidl_vec<hidl_string>>();
+ auto listRet = timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::list,
+ [fqInstanceNames](const auto& names) { *fqInstanceNames = names; });
if (!listRet.isOk()) {
err() << "Error: Failed to list services for " << mode << ": "
<< listRet.description() << std::endl;
@@ -596,7 +621,7 @@
Status status = OK;
std::map<std::string, TableEntry> allTableEntries;
- for (const auto &fqInstanceName : fqInstanceNames) {
+ for (const auto& fqInstanceName : *fqInstanceNames) {
// create entry and default assign all fields.
TableEntry& entry = allTableEntries[fqInstanceName];
entry.interfaceName = fqInstanceName;
@@ -623,7 +648,8 @@
const auto pair = splitFirst(entry->interfaceName, '/');
const auto &serviceName = pair.first;
const auto &instanceName = pair.second;
- auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+ auto getRet = timeoutIPC(mLshal.getIpcCallWait(), manager, &IServiceManager::get, serviceName,
+ instanceName);
if (!getRet.isOk()) {
handleError(TRANSACTION_ERROR,
"cannot be fetched from service manager:" + getRet.description());
@@ -637,30 +663,33 @@
// getDebugInfo
do {
- DebugInfo debugInfo;
- auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
- debugInfo = received;
- });
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ // However, there's no need to lock because if debugRet.isOk(), the background thread has
+ // already ended, so it is safe to dereference debugInfo.
+ auto debugInfo = std::make_shared<DebugInfo>();
+ auto debugRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::getDebugInfo,
+ [debugInfo](const auto& received) { *debugInfo = received; });
if (!debugRet.isOk()) {
handleError(TRANSACTION_ERROR,
"debugging information cannot be retrieved: " + debugRet.description());
break; // skip getPidInfo
}
- entry->serverPid = debugInfo.pid;
- entry->serverObjectAddress = debugInfo.ptr;
- entry->arch = fromBaseArchitecture(debugInfo.arch);
+ entry->serverPid = debugInfo->pid;
+ entry->serverObjectAddress = debugInfo->ptr;
+ entry->arch = fromBaseArchitecture(debugInfo->arch);
- if (debugInfo.pid != NO_PID) {
- const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
+ if (debugInfo->pid != NO_PID) {
+ const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo->pid);
if (pidInfo == nullptr) {
handleError(IO_ERROR,
- "no information for PID " + std::to_string(debugInfo.pid) +
- ", are you root?");
+ "no information for PID " + std::to_string(debugInfo->pid) +
+ ", are you root?");
break;
}
- if (debugInfo.ptr != NO_PTR) {
- auto it = pidInfo->refPids.find(debugInfo.ptr);
+ if (debugInfo->ptr != NO_PTR) {
+ auto it = pidInfo->refPids.find(debugInfo->ptr);
if (it != pidInfo->refPids.end()) {
entry->clientPids = it->second;
}
@@ -672,39 +701,46 @@
// hash
do {
- ssize_t hashIndex = -1;
- auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
- for (size_t i = 0; i < c.size(); ++i) {
- if (serviceName == c[i]) {
- hashIndex = static_cast<ssize_t>(i);
- break;
- }
- }
- });
+ // The lambda function may be executed asynchrounously because it is passed to timeoutIPC,
+ // even though the interface function call is synchronous.
+ auto hashIndexStore = std::make_shared<ssize_t>(-1);
+ auto ifaceChainRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::interfaceChain,
+ [hashIndexStore, serviceName](const auto& c) {
+ for (size_t i = 0; i < c.size(); ++i) {
+ if (serviceName == c[i]) {
+ *hashIndexStore = static_cast<ssize_t>(i);
+ break;
+ }
+ }
+ });
if (!ifaceChainRet.isOk()) {
handleError(TRANSACTION_ERROR,
"interfaceChain fails: " + ifaceChainRet.description());
break; // skip getHashChain
}
+ // if ifaceChainRet.isOk(), the background thread has already ended, so it is safe to
+ // dereference hashIndex without any locking.
+ auto hashIndex = *hashIndexStore;
if (hashIndex < 0) {
handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
break; // skip getHashChain
}
- auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
- if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
- handleError(BAD_IMPL,
- "interfaceChain indicates position " + std::to_string(hashIndex) +
- " but getHashChain returns " + std::to_string(hashChain.size()) +
- " hashes");
- return;
- }
-
- auto&& hashArray = hashChain[hashIndex];
- entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
- });
+ // See comments about hashIndex above.
+ auto hashChain = std::make_shared<hidl_vec<hidl_array<uint8_t, 32>>>();
+ auto hashRet = timeoutIPC(mLshal.getIpcCallWait(), service, &IBase::getHashChain,
+ [hashChain](const auto& ret) { *hashChain = std::move(ret); });
if (!hashRet.isOk()) {
handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
}
+ if (static_cast<size_t>(hashIndex) >= hashChain->size()) {
+ handleError(BAD_IMPL,
+ "interfaceChain indicates position " + std::to_string(hashIndex) +
+ " but getHashChain returns " + std::to_string(hashChain->size()) +
+ " hashes");
+ } else {
+ auto&& hashArray = (*hashChain)[hashIndex];
+ entry->hash = android::base::HexString(hashArray.data(), hashArray.size());
+ }
} while (0);
if (status == OK) {
entry->serviceStatus = ServiceStatus::ALIVE;
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index a5f98c2..6115da7 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -250,5 +250,17 @@
return mPassthroughManager;
}
+void Lshal::setWaitTimeForTest(std::chrono::milliseconds ipcCallWait,
+ std::chrono::milliseconds debugDumpWait) {
+ mIpcCallWait = ipcCallWait;
+ mDebugDumpWait = debugDumpWait;
+}
+std::chrono::milliseconds Lshal::getIpcCallWait() const {
+ return mIpcCallWait;
+}
+std::chrono::milliseconds Lshal::getDebugDumpWait() const {
+ return mDebugDumpWait;
+}
+
} // namespace lshal
} // namespace android
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 50279d4..cb2820c 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <iostream>
#include <string>
@@ -58,6 +59,11 @@
void forEachCommand(const std::function<void(const Command* c)>& f) const;
+ void setWaitTimeForTest(std::chrono::milliseconds ipcCallWait,
+ std::chrono::milliseconds debugDumpWait);
+ std::chrono::milliseconds getIpcCallWait() const;
+ std::chrono::milliseconds getDebugDumpWait() const;
+
private:
Status parseArgs(const Arg &arg);
@@ -70,6 +76,9 @@
std::vector<std::unique_ptr<Command>> mRegisteredCommands;
+ std::chrono::milliseconds mIpcCallWait{500};
+ std::chrono::milliseconds mDebugDumpWait{10000};
+
DISALLOW_COPY_AND_ASSIGN(Lshal);
};
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index e8d22d9..805e8dc 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -27,8 +27,6 @@
namespace android {
namespace lshal {
-static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500};
-
class BackgroundTaskState {
public:
explicit BackgroundTaskState(std::function<void(void)> &&func)
@@ -96,12 +94,5 @@
return ret;
}
-template<class Function, class I, class... Args>
-typename std::result_of<Function(I *, Args...)>::type
-timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) {
- return timeoutIPC(IPC_CALL_WAIT, interfaceObject, func, args...);
-}
-
-
} // namespace lshal
} // namespace android
diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp
index 55326ea..de0e171 100644
--- a/cmds/sfdo/sfdo.cpp
+++ b/cmds/sfdo/sfdo.cpp
@@ -16,7 +16,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <any>
-#include <unordered_map>
+#include <map>
#include <cutils/properties.h>
#include <sys/resource.h>
@@ -29,18 +29,28 @@
using namespace android;
-std::unordered_map<std::string, std::any> g_functions;
+std::map<std::string, std::any> g_functions;
-const std::unordered_map<std::string, std::string> g_function_details = {
- {"DebugFlash", "[optional(delay)] Perform a debug flash."},
- {"FrameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
- {"scheduleComposite", "Force composite ahead of next VSYNC."},
- {"scheduleCommit", "Force commit ahead of next VSYNC."},
- {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
+enum class ParseToggleResult {
+ kError,
+ kFalse,
+ kTrue,
+};
+
+const std::map<std::string, std::string> g_function_details = {
+ {"debugFlash", "[optional(delay)] Perform a debug flash."},
+ {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
+ {"scheduleComposite", "Force composite ahead of next VSYNC."},
+ {"scheduleCommit", "Force commit ahead of next VSYNC."},
+ {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
+ {"forceClientComposition",
+ "[enabled | disabled] When enabled, it disables "
+ "Hardware Overlays, and routes all window composition to the GPU. This can "
+ "help check if there is a bug in HW Composer."},
};
static void ShowUsage() {
- std::cout << "usage: sfdo [help, FrameRateIndicator show, DebugFlash enabled, ...]\n\n";
+ std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n";
for (const auto& sf : g_functions) {
const std::string fn = sf.first;
std::string fdetails = "TODO";
@@ -50,7 +60,26 @@
}
}
-int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+// Returns 1 for positive keywords and 0 for negative keywords.
+// If the string does not match any it will return -1.
+ParseToggleResult parseToggle(const char* str) {
+ const std::unordered_set<std::string> positive{"1", "true", "y", "yes",
+ "on", "enabled", "show"};
+ const std::unordered_set<std::string> negative{"0", "false", "n", "no",
+ "off", "disabled", "hide"};
+
+ const std::string word(str);
+ if (positive.count(word)) {
+ return ParseToggleResult::kTrue;
+ }
+ if (negative.count(word)) {
+ return ParseToggleResult::kFalse;
+ }
+
+ return ParseToggleResult::kError;
+}
+
+int frameRateIndicator(int argc, char** argv) {
bool hide = false, show = false;
if (argc == 3) {
show = strcmp(argv[2], "show") == 0;
@@ -60,13 +89,13 @@
if (show || hide) {
ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show);
} else {
- std::cerr << "Incorrect usage of FrameRateIndicator. Missing [hide | show].\n";
+ std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n";
return -1;
}
return 0;
}
-int DebugFlash([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+int debugFlash(int argc, char** argv) {
int delay = 0;
if (argc == 3) {
delay = atoi(argv[2]) == 0;
@@ -86,14 +115,40 @@
return 0;
}
+int forceClientComposition(int argc, char** argv) {
+ bool enabled = true;
+ // A valid command looks like this:
+ // adb shell sfdo forceClientComposition enabled
+ if (argc >= 3) {
+ const ParseToggleResult toggle = parseToggle(argv[2]);
+ if (toggle == ParseToggleResult::kError) {
+ std::cerr << "Incorrect usage of forceClientComposition. "
+ "Missing [enabled | disabled].\n";
+ return -1;
+ }
+ if (argc > 3) {
+ std::cerr << "Too many arguments after [enabled | disabled]. "
+ "Ignoring extra arguments.\n";
+ }
+ enabled = (toggle == ParseToggleResult::kTrue);
+ } else {
+ std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n";
+ return -1;
+ }
+
+ ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled);
+ return 0;
+}
+
int main(int argc, char** argv) {
std::cout << "Execute SurfaceFlinger internal commands.\n";
std::cout << "sfdo requires to be run with root permissions..\n";
- g_functions["FrameRateIndicator"] = FrameRateIndicator;
- g_functions["DebugFlash"] = DebugFlash;
+ g_functions["frameRateIndicator"] = frameRateIndicator;
+ g_functions["debugFlash"] = debugFlash;
g_functions["scheduleComposite"] = scheduleComposite;
g_functions["scheduleCommit"] = scheduleCommit;
+ g_functions["forceClientComposition"] = forceClientComposition;
if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) {
std::cout << "Running: " << argv[1] << "\n";
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 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/android/thermal.h b/include/android/thermal.h
index 1f477f8..0b57e93 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -111,7 +111,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
@@ -222,6 +222,70 @@
float AThermal_getThermalHeadroom(AThermalManager *manager,
int forecastSeconds) __INTRODUCED_IN(31);
+/**
+ * This struct defines an instance of headroom threshold value and its status.
+ * <p>
+ * The value should be monotonically non-decreasing as the thermal status increases.
+ * For {@link ATHERMAL_STATUS_SEVERE}, its headroom threshold is guaranteed to
+ * be 1.0f. For status below severe status, the value should be lower or equal
+ * to 1.0f, and for status above severe, the value should be larger or equal to 1.0f.
+ * <p>
+ * Also see {@link AThermal_getThermalHeadroom} for explanation on headroom, and
+ * {@link AThermal_getThermalHeadroomThresholds} for how to use this.
+ */
+struct AThermalHeadroomThreshold {
+ float headroom;
+ AThermalStatus thermalStatus;
+};
+
+/**
+ * Gets the thermal headroom thresholds for all available thermal status.
+ *
+ * A thermal status will only exist in output if the device manufacturer has the
+ * corresponding threshold defined for at least one of its slow-moving skin temperature
+ * sensors. If it's set, one should also expect to get it from
+ * {@link #AThermal_getCurrentThermalStatus} or {@link AThermal_StatusCallback}.
+ * <p>
+ * The headroom threshold is used to interpret the possible thermal throttling status based on
+ * the headroom prediction. For example, if the headroom threshold for
+ * {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
+ * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system
+ * could be in lightly throttled state if the workload remains the same. The app can consider
+ * taking actions according to the nearest throttling status the difference between the headroom and
+ * the threshold.
+ * <p>
+ * For new devices it's guaranteed to have a single sensor, but for older devices with multiple
+ * sensors reporting different threshold values, the minimum threshold is taken to be conservative
+ * on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value
+ * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above
+ * will always come with lightly throttled state
+ * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower
+ * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}).
+ * While it's always guaranteed that the device won't be throttled heavier than the unmet
+ * threshold's state, so a real-time headroom of 0.75 will never come with
+ * {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with
+ * {@link ATHERMAL_STATUS_LIGHT} but {@link #ATHERMAL_STATUS_NONE}.
+ * <p>
+ * The returned list of thresholds is cached on first successful query and owned by the thermal
+ * manager, which will not change between calls to this function. The caller should only need to
+ * free the manager with {@link AThermal_releaseManager}.
+ *
+ * @param manager The manager instance to use.
+ * Acquired via {@link AThermal_acquireManager}.
+ * @param outThresholds non-null output pointer to null AThermalHeadroomThreshold pointer, which
+ * will be set to the cached array of thresholds if thermal thresholds are supported
+ * by the system or device, otherwise nullptr or unmodified.
+ * @param size non-null output pointer whose value will be set to the size of the threshold array
+ * or 0 if it's not supported.
+ * @return 0 on success
+ * EINVAL if outThresholds or size_t is nullptr, or *outThresholds is not nullptr.
+ * EPIPE if communication with the system service has failed.
+ * ENOSYS if the feature is disabled by the current system.
+ */
+int AThermal_getThermalHeadroomThresholds(AThermalManager* manager,
+ const AThermalHeadroomThreshold ** outThresholds,
+ size_t* size) __INTRODUCED_IN(35);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/binder/unique_fd.h b/include/binder/unique_fd.h
new file mode 120000
index 0000000..433c968
--- /dev/null
+++ b/include/binder/unique_fd.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/unique_fd.h
\ No newline at end of file
diff --git a/include/ftl/details/function.h b/include/ftl/details/function.h
new file mode 100644
index 0000000..35c5a8b
--- /dev/null
+++ b/include/ftl/details/function.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <type_traits>
+
+namespace android::ftl::details {
+
+// The maximum allowed value for the template argument `N` in
+// `ftl::Function<F, N>`.
+constexpr size_t kFunctionMaximumN = 14;
+
+// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member
+// function type `Ret(Args...)`.
+
+template <typename>
+struct remove_member_function_pointer;
+
+template <typename Class, typename Ret, typename... Args>
+struct remove_member_function_pointer<Ret (Class::*)(Args...)> {
+ using type = Ret(Args...);
+};
+
+template <typename Class, typename Ret, typename... Args>
+struct remove_member_function_pointer<Ret (Class::*)(Args...) const> {
+ using type = Ret(Args...);
+};
+
+template <auto MemberFunction>
+using remove_member_function_pointer_t =
+ typename remove_member_function_pointer<decltype(MemberFunction)>::type;
+
+// Helper functions for binding to the supported targets.
+
+template <typename Ret, typename... Args>
+auto bind_opaque_no_op() -> Ret (*)(void*, Args...) {
+ return [](void*, Args...) -> Ret {
+ if constexpr (!std::is_void_v<Ret>) {
+ return Ret{};
+ }
+ };
+}
+
+template <typename F, typename Ret, typename... Args>
+auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) {
+ return [](void* opaque, Args... args) -> Ret {
+ return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...);
+ };
+}
+
+template <auto MemberFunction, typename Class, typename Ret, typename... Args>
+auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) {
+ return [instance](Args... args) -> Ret {
+ return std::invoke(MemberFunction, instance, std::forward<Args>(args)...);
+ };
+}
+
+template <auto FreeFunction, typename Ret, typename... Args>
+auto bind_free_function(Ret (*)(Args...) = nullptr) {
+ return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); };
+}
+
+// Traits class for the opaque storage used by Function.
+
+template <std::size_t N>
+struct function_opaque_storage {
+ // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size,
+ // which allows a lambda with zero or one capture args.
+ using type = std::array<std::intptr_t, N + 1>;
+
+ template <typename S>
+ static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>;
+
+ template <typename S>
+ static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>;
+
+ template <typename S>
+ static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type);
+
+ template <typename S>
+ static constexpr bool require_alignment_compatible =
+ std::alignment_of_v<S> <= std::alignment_of_v<type>;
+
+ // Copies `src` into the opaque storage, and returns that storage.
+ template <typename S>
+ static type opaque_copy(const S& src) {
+ // TODO: Replace with C++20 concepts/constraints which can give more details.
+ static_assert(require_trivially_copyable<S>,
+ "ftl::Function can only store lambdas that capture trivially copyable data.");
+ static_assert(
+ require_trivially_destructible<S>,
+ "ftl::Function can only store lambdas that capture trivially destructible data.");
+ static_assert(require_will_fit_in_opaque_storage<S>,
+ "ftl::Function has limited storage for lambda captured state. Maybe you need to "
+ "increase N?");
+ static_assert(require_alignment_compatible<S>);
+
+ type opaque;
+ std::memcpy(opaque.data(), &src, sizeof(S));
+ return opaque;
+ }
+};
+
+// Traits class to help determine the template parameters to use for a ftl::Function, given a
+// function object.
+
+template <typename F, typename = decltype(&F::operator())>
+struct function_traits {
+ // The function type `F` with which to instantiate the `Function<F, N>` template.
+ using type = remove_member_function_pointer_t<&F::operator()>;
+
+ // The (minimum) size `N` with which to instantiate the `Function<F, N>` template.
+ static constexpr std::size_t size =
+ (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t);
+};
+
+} // namespace android::ftl::details
diff --git a/include/ftl/function.h b/include/ftl/function.h
new file mode 100644
index 0000000..3538ca4
--- /dev/null
+++ b/include/ftl/function.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/details/function.h>
+
+namespace android::ftl {
+
+// ftl::Function<F, N> is a container for function object, and can mostly be used in place of
+// std::function<F>.
+//
+// Unlike std::function<F>, a ftl::Function<F, N>:
+//
+// * Uses a static amount of memory (controlled by N), and never any dynamic allocation.
+// * Satisfies the std::is_trivially_copyable<> trait.
+// * Satisfies the std::is_trivially_destructible<> trait.
+//
+// However those same limits are also required from the contained function object in turn.
+//
+// The size of a ftl::Function<F, N> is guaranteed to be:
+//
+// sizeof(std::intptr_t) * (N + 2)
+//
+// A ftl::Function<F, N> can always be implicitly converted to a larger size ftl::Function<F, M>.
+// Trying to convert the other way leads to a compilation error.
+//
+// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns
+// false in this state. It is undefined behavior to attempt to invoke the function in this state.
+//
+// The ftl::Function<F, N> can also be constructed or assigned from ftl::no_op. This sets up the
+// ftl::Function to be non-empty, with a function that when called does nothing except
+// default-constructs a return value.
+//
+// The ftl::make_function() helpers construct a ftl::Function<F, N>, including deducing the
+// values of F and N from the arguments it is given.
+//
+// The static ftl::Function<F, N>::make() helpers construct a ftl::Function<F, N> without that
+// deduction, and also allow for implicit argument conversion if the target being called needs them.
+//
+// The construction helpers allow any of the following types of functions to be stored:
+//
+// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small
+// capture, or other "functor". The requirements are:
+//
+// 1) The function object must be trivial to destroy (in fact, the destructor will never
+// actually be called once copied to the internal storage).
+// 2) The function object must be trivial to copy (the raw bytes will be copied as the
+// ftl::Function<F, N> is copied/moved).
+// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1),
+// and it cannot require stricter alignment than alignof(std::intptr_t).
+//
+// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is
+// enough to capture `this`, which is why N=0 is the default.
+//
+// * A member function, with the address passed as the template value argument to the construction
+// helper function, along with the instance pointer needed to invoke it passed as an ordinary
+// argument.
+//
+// ftl::make_function<&Class::member_function>(this);
+//
+// Note that the indicated member function will be invoked non-virtually. If you need it to be
+// invoked virtually, you should invoke it yourself with a small lambda like so:
+//
+// ftl::function([this] { virtual_member_function(); });
+//
+// * An ordinary function ("free function"), with the address of the function passed as a template
+// value argument.
+//
+// ftl::make_function<&std::atoi>();
+//
+// As with the member function helper, as the function is known at compile time, it will be called
+// directly.
+//
+// Example usage:
+//
+// class MyClass {
+// public:
+// void on_event() const {}
+// int on_string(int*, std::string_view) { return 1; }
+//
+// auto get_function() {
+// return ftl::function([this] { on_event(); });
+// }
+// } cls;
+//
+// // A function container with no arguments, and returning no value.
+// ftl::Function<void()> f;
+//
+// // Construct a ftl::Function containing a small lambda.
+// f = cls.get_function();
+//
+// // Construct a ftl::Function that calls `cls.on_event()`.
+// f = ftl::function<&MyClass::on_event>(&cls);
+//
+// // Create a do-nothing function.
+// f = ftl::no_op;
+//
+// // Invoke the contained function.
+// f();
+//
+// // Also invokes it.
+// std::invoke(f);
+//
+// // Create a typedef to give a more meaningful name and bound the size.
+// using MyFunction = ftl::Function<int(std::string_view), 2>;
+// int* ptr = nullptr;
+// auto f1 = MyFunction::make_function(
+// [cls = &cls, ptr](std::string_view sv) {
+// return cls->on_string(ptr, sv);
+// });
+// int r = f1("abc"sv);
+//
+// // Returns a default-constructed int (0).
+// f1 = ftl::no_op;
+// r = f1("abc"sv);
+// assert(r == 0);
+
+template <typename F, std::size_t N = 0>
+class Function;
+
+// Used to construct a Function that does nothing.
+struct NoOpTag {};
+
+constexpr NoOpTag no_op;
+
+// Detects that a type is a `ftl::Function<F, N>` regardless of what `F` and `N` are.
+template <typename>
+struct is_function : public std::false_type {};
+
+template <typename F, std::size_t N>
+struct is_function<Function<F, N>> : public std::true_type {};
+
+template <typename T>
+constexpr bool is_function_v = is_function<T>::value;
+
+template <typename Ret, typename... Args, std::size_t N>
+class Function<Ret(Args...), N> final {
+ // Enforce a valid size, with an arbitrary maximum allowed size for the container of
+ // sizeof(std::intptr_t) * 16, though that maximum can be relaxed.
+ static_assert(N <= details::kFunctionMaximumN);
+
+ using OpaqueStorageTraits = details::function_opaque_storage<N>;
+
+ public:
+ // Defining result_type allows ftl::Function to be substituted for std::function.
+ using result_type = Ret;
+
+ // Constructs an empty ftl::Function.
+ Function() = default;
+
+ // Constructing or assigning from nullptr_t also creates an empty ftl::Function.
+ Function(std::nullptr_t) {}
+ Function& operator=(std::nullptr_t) { return *this = Function(nullptr); }
+
+ // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which
+ // returns a default constructed return value.
+ Function(NoOpTag) : function_(details::bind_opaque_no_op<Ret, Args...>()) {}
+ Function& operator=(NoOpTag) { return *this = Function(no_op); }
+
+ // Constructing/assigning from a function object stores a copy of that function object, however:
+ // * It must be trivially copyable, as the implementation makes a copy with memcpy().
+ // * It must be trivially destructible, as the implementation doesn't destroy the copy!
+ // * It must fit in the limited internal storage, which enforces size/alignment restrictions.
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>>
+ Function(const F& f)
+ : opaque_(OpaqueStorageTraits::opaque_copy(f)),
+ function_(details::bind_opaque_function_object<F, Ret, Args...>(f)) {}
+
+ template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>>
+ Function& operator=(const F& f) noexcept {
+ return *this = Function{OpaqueStorageTraits::opaque_copy(f),
+ details::bind_opaque_function_object<F, Ret, Args...>(f)};
+ }
+
+ // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else.
+
+ template <std::size_t M>
+ Function(const Function<Ret(Args...), M>& other)
+ : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {}
+
+ template <std::size_t M>
+ auto& operator=(const Function<Ret(Args...), M>& other) {
+ return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_};
+ }
+
+ // Returns true if a function is set.
+ explicit operator bool() const { return function_ != nullptr; }
+
+ // Checks if the other function has the same contents as this one.
+ bool operator==(const Function& other) const {
+ return other.opaque_ == opaque_ && other.function_ == function_;
+ }
+ bool operator!=(const Function& other) const { return !operator==(other); }
+
+ // Alternative way of testing for a function being set.
+ bool operator==(std::nullptr_t) const { return function_ == nullptr; }
+ bool operator!=(std::nullptr_t) const { return function_ != nullptr; }
+
+ // Invokes the function.
+ Ret operator()(Args... args) const {
+ return std::invoke(function_, opaque_.data(), std::forward<Args>(args)...);
+ }
+
+ // Creation helper for function objects, such as lambdas.
+ template <typename F>
+ static auto make(const F& f) -> decltype(Function{f}) {
+ return Function{f};
+ }
+
+ // Creation helper for a class pointer and a compile-time chosen member function to call.
+ template <auto MemberFunction, typename Class>
+ static auto make(Class* instance) -> decltype(Function{
+ details::bind_member_function<MemberFunction>(instance,
+ static_cast<Ret (*)(Args...)>(nullptr))}) {
+ return Function{details::bind_member_function<MemberFunction>(
+ instance, static_cast<Ret (*)(Args...)>(nullptr))};
+ }
+
+ // Creation helper for a compile-time chosen free function to call.
+ template <auto FreeFunction>
+ static auto make() -> decltype(Function{
+ details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}) {
+ return Function{
+ details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))};
+ }
+
+ private:
+ // Needed so a Function<F, M> can be converted to a Function<F, N>.
+ template <typename, std::size_t>
+ friend class Function;
+
+ // The function pointer type of function stored in `function_`. The first argument is always
+ // `&opaque_`.
+ using StoredFunction = Ret(void*, Args...);
+
+ // The type of the opaque storage, used to hold an appropriate function object.
+ // The type stored here is ONLY known to the StoredFunction.
+ // We always use at least one std::intptr_t worth of storage, and always a multiple of that size.
+ using OpaqueStorage = typename OpaqueStorageTraits::type;
+
+ // Internal constructor for creating from a raw opaque blob + function pointer.
+ Function(const OpaqueStorage& opaque, StoredFunction* function)
+ : opaque_(opaque), function_(function) {}
+
+ // Note: `mutable` so that `operator() const` can use it.
+ mutable OpaqueStorage opaque_{};
+ StoredFunction* function_{nullptr};
+};
+
+// Makes a ftl::Function given a function object `F`.
+template <typename F, typename T = details::function_traits<F>>
+Function(const F&) -> Function<typename T::type, T::size>;
+
+template <typename F>
+auto make_function(const F& f) -> decltype(Function{f}) {
+ return Function{f};
+}
+
+// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`.
+template <auto MemberFunction, typename Class>
+auto make_function(Class* instance)
+ -> decltype(Function{details::bind_member_function<MemberFunction>(
+ instance,
+ static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}) {
+ return Function{details::bind_member_function<MemberFunction>(
+ instance, static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))};
+}
+
+// Makes a ftl::Function given an ordinary free function.
+template <auto FreeFunction>
+auto make_function() -> decltype(Function{
+ details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}) {
+ return Function{
+ details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))};
+}
+
+} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 7457496..b0eceef 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -130,9 +130,9 @@
"isActive=[%d]",
ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
- orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
- physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
- deviceHeight, isActive);
+ static_cast<int>(orientation), logicalLeft, logicalTop, logicalRight,
+ logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom,
+ deviceWidth, deviceHeight, isActive);
}
};
diff --git a/include/input/Input.h b/include/input/Input.h
index bd544b5..7b253a5 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -515,6 +515,8 @@
PointerProperties& operator=(const PointerProperties&) = default;
};
+std::ostream& operator<<(std::ostream& out, const PointerProperties& properties);
+
// TODO(b/211379801) : Use a strong type from ftl/mixins.h instead
using DeviceId = int32_t;
@@ -1138,6 +1140,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/InputTransport.h b/include/input/InputTransport.h
index f20829c..750e170 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -240,7 +240,7 @@
android::base::unique_fd fd, sp<IBinder> token);
InputChannel() = default;
InputChannel(const InputChannel& other)
- : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){};
+ : mName(other.mName), mFd(other.dupFd()), mToken(other.mToken){};
InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
~InputChannel() override;
/**
@@ -317,7 +317,7 @@
if (fstat(mFd.get(), &lhs) != 0) {
return false;
}
- if (fstat(inputChannel.getFd(), &rhs) != 0) {
+ if (fstat(inputChannel.getFd().get(), &rhs) != 0) {
return false;
}
// If file descriptors are pointing to same inode they are duplicated fds.
@@ -329,7 +329,7 @@
base::unique_fd dupFd() const;
std::string mName;
- android::base::unique_fd mFd;
+ base::unique_fd mFd;
sp<IBinder> mToken;
};
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/MotionPredictor.h b/include/input/MotionPredictor.h
index 8797962..3b6e401 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
+#include <optional>
#include <string>
#include <unordered_map>
@@ -57,20 +58,23 @@
*/
class MotionPredictor {
public:
+ using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
+
/**
* Parameters:
* predictionTimestampOffsetNanos: additional, constant shift to apply to the target
* prediction time. The prediction will target the time t=(prediction time +
* predictionTimestampOffsetNanos).
*
- * modelPath: filesystem path to a TfLiteMotionPredictorModel flatbuffer, or nullptr to use the
- * default model path.
- *
- * checkEnableMotionPredition: the function to check whether the prediction should run. Used to
+ * checkEnableMotionPrediction: the function to check whether the prediction should run. Used to
* provide an additional way of turning prediction on and off. Can be toggled at runtime.
+ *
+ * reportAtomFunction: the function that will be called to report prediction metrics. If
+ * omitted, the implementation will choose a default metrics reporting mechanism.
*/
MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
- std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
+ std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled,
+ ReportAtomFunction reportAtomFunction = {});
/**
* Record the actual motion received by the view. This event will be used for calculating the
@@ -95,6 +99,8 @@
std::optional<MotionEvent> mLastEvent;
std::optional<MotionPredictorMetricsManager> mMetricsManager;
+
+ const ReportAtomFunction mReportAtomFunction;
};
} // namespace android
diff --git a/include/input/MotionPredictorMetricsManager.h b/include/input/MotionPredictorMetricsManager.h
index 12e50ba..38472d8 100644
--- a/include/input/MotionPredictorMetricsManager.h
+++ b/include/input/MotionPredictorMetricsManager.h
@@ -18,7 +18,6 @@
#include <cstdint>
#include <functional>
#include <limits>
-#include <optional>
#include <vector>
#include <input/Input.h> // for MotionEvent
@@ -37,15 +36,33 @@
*
* This class stores AggregatedStrokeMetrics, updating them as new MotionEvents are passed in. When
* onRecord receives an UP or CANCEL event, this indicates the end of the stroke, and the final
- * AtomFields are computed and reported to the stats library.
+ * AtomFields are computed and reported to the stats library. The number of atoms reported is equal
+ * to the value of `maxNumPredictions` passed to the constructor. Each atom corresponds to one
+ * "prediction time bucket" — the amount of time into the future being predicted.
*
* If mMockLoggedAtomFields is set, the batch of AtomFields that are reported to the stats library
* for one stroke are also stored in mMockLoggedAtomFields at the time they're reported.
*/
class MotionPredictorMetricsManager {
public:
- // Note: the MetricsManager assumes that the input interval equals the prediction interval.
- MotionPredictorMetricsManager(nsecs_t predictionInterval, size_t maxNumPredictions);
+ struct AtomFields;
+
+ using ReportAtomFunction = std::function<void(const AtomFields&)>;
+
+ static void defaultReportAtomFunction(const AtomFields& atomFields);
+
+ // Parameters:
+ // • predictionInterval: the time interval between successive prediction target timestamps.
+ // Note: the MetricsManager assumes that the input interval equals the prediction interval.
+ // • maxNumPredictions: the maximum number of distinct target timestamps the prediction model
+ // will generate predictions for. The MetricsManager reports this many atoms per stroke.
+ // • [Optional] reportAtomFunction: the function that will be called to report metrics. If
+ // omitted (or if an empty function is given), the `stats_write(…)` function from the Android
+ // stats library will be used.
+ MotionPredictorMetricsManager(
+ nsecs_t predictionInterval,
+ size_t maxNumPredictions,
+ ReportAtomFunction reportAtomFunction = defaultReportAtomFunction);
// This method should be called once for each call to MotionPredictor::record, receiving the
// forwarded MotionEvent argument.
@@ -121,7 +138,7 @@
// magnitude makes it unobtainable in practice.)
static const int NO_DATA_SENTINEL = std::numeric_limits<int32_t>::min();
- // Final metrics reported in the atom.
+ // Final metric values reported in the atom.
struct AtomFields {
int deltaTimeBucketMilliseconds = 0;
@@ -140,15 +157,6 @@
int scaleInvariantOffTrajectoryRmse = NO_DATA_SENTINEL; // millipixels
};
- // Allow tests to pass in a mock AtomFields pointer.
- //
- // When metrics are reported to the stats library on stroke end, they will also be written to
- // mockLoggedAtomFields, overwriting existing data. The size of mockLoggedAtomFields will equal
- // the number of calls to stats_write for that stroke.
- void setMockLoggedAtomFields(std::vector<AtomFields>* mockLoggedAtomFields) {
- mMockLoggedAtomFields = mockLoggedAtomFields;
- }
-
private:
// The interval between consecutive predictions' target timestamps. We assume that the input
// interval also equals this value.
@@ -172,11 +180,7 @@
std::vector<AggregatedStrokeMetrics> mAggregatedMetrics;
std::vector<AtomFields> mAtomFields;
- // Non-owning pointer to the location of mock AtomFields. If present, will be filled with the
- // values reported to stats_write on each batch of reported metrics.
- //
- // This pointer must remain valid as long as the MotionPredictorMetricsManager exists.
- std::vector<AtomFields>* mMockLoggedAtomFields = nullptr;
+ const ReportAtomFunction mReportAtomFunction;
// Helper methods for the implementation of onRecord and onPredict.
@@ -196,10 +200,7 @@
// Computes the atom fields to mAtomFields from the values in mAggregatedMetrics.
void computeAtomFields();
- // Reports the metrics given by the current data in mAtomFields:
- // • If on an Android device, reports the metrics to stats_write.
- // • If mMockLoggedAtomFields is present, it will be overwritten with logged metrics, with one
- // AtomFields element per call to stats_write.
+ // Reports the current data in mAtomFields by calling mReportAtomFunction.
void reportMetrics();
};
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 63c0e40..3470be4 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -20,6 +20,7 @@
#include <map>
#include <optional>
#include <set>
+#include <sstream>
#include <string>
#include <vector>
@@ -33,6 +34,13 @@
return bitset.to_string();
}
+template <class T>
+std::string streamableToString(const T& streamable) {
+ std::stringstream out;
+ out << streamable;
+ return out.str();
+}
+
template <typename T>
inline std::string constToString(const T& v) {
return std::to_string(v);
@@ -109,11 +117,12 @@
template <typename T>
std::string dumpVector(const std::vector<T>& values,
std::string (*valueToString)(const T&) = constToString) {
- std::string dump = valueToString(values[0]);
- for (size_t i = 1; i < values.size(); i++) {
- dump += ", " + valueToString(values[i]);
+ std::string out;
+ for (const auto& value : values) {
+ out += out.empty() ? "[" : ", ";
+ out += valueToString(value);
}
- return dump;
+ return out.empty() ? "[]" : (out + "]");
}
const char* toString(bool value);
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/gui/include/gui/Flags.h b/include/private/thermal_private.h
similarity index 63%
copy from libs/gui/include/gui/Flags.h
copy to include/private/thermal_private.h
index a2cff56..951d953 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/include/private/thermal_private.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * 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.
@@ -14,9 +14,18 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_PRIVATE_NATIVE_THERMAL_H
+#define ANDROID_PRIVATE_NATIVE_THERMAL_H
-// 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
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+/**
+ * For testing only.
+ */
+void AThermal_setIThermalServiceForTesting(void* iThermalService);
+
+__END_DECLS
+
+#endif // ANDROID_PRIVATE_NATIVE_THERMAL_H
\ No newline at end of file
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 620c23c..ae0fb01 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -22,23 +22,51 @@
}
cc_library_headers {
- name: "libbinder_headers",
+ name: "libbinder_headers_base",
export_include_dirs: ["include"],
vendor_available: true,
recovery_available: true,
host_supported: true,
- // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
+
+ header_libs: [
+ "libbinder_headers_platform_shared",
+ ],
+ export_header_lib_headers: [
+ "libbinder_headers_platform_shared",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ "com.android.media.swcodec",
+ ],
+ min_sdk_version: "29",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
+cc_library_headers {
+ name: "libbinder_headers",
+ vendor_available: true,
+ recovery_available: true,
+ host_supported: true,
native_bridge_supported: true,
header_libs: [
"libbase_headers",
- "libbinder_headers_platform_shared",
+ "libbinder_headers_base",
"libcutils_headers",
"libutils_headers",
],
export_header_lib_headers: [
"libbase_headers",
- "libbinder_headers_platform_shared",
+ "libbinder_headers_base",
"libcutils_headers",
"libutils_headers",
],
@@ -87,24 +115,27 @@
"RpcSession.cpp",
"RpcServer.cpp",
"RpcState.cpp",
+ "RpcTransportRaw.cpp",
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
- "Trace.cpp",
"Utils.cpp",
- ],
-
- shared_libs: [
- "libcutils",
- "libutils",
- ],
-
- static_libs: [
- "libbase",
+ "file.cpp",
],
header_libs: [
- "libbinder_headers",
+ "libbinder_headers_base",
+ ],
+
+ cflags: [
+ "-Wextra",
+ "-Wextra-semi",
+ "-Werror",
+ "-Wzero-as-null-pointer-constant",
+ "-Wreorder-init-list",
+ "-Wunused-const-variable",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
}
@@ -120,7 +151,6 @@
srcs: [
"OS_android.cpp",
"OS_unix_base.cpp",
- "RpcTransportRaw.cpp",
],
target: {
@@ -135,15 +165,6 @@
export_aidl_headers: true,
},
- cflags: [
- "-Wextra",
- "-Wextra-semi",
- "-Werror",
- "-Wzero-as-null-pointer-constant",
- "-Wreorder-init-list",
- "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
- ],
product_variables: {
debuggable: {
cflags: [
@@ -154,11 +175,18 @@
},
shared_libs: [
+ "libcutils",
"liblog",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
],
header_libs: [
"jni_headers",
+ "libbinder_headers",
],
export_header_lib_headers: [
@@ -215,12 +243,24 @@
host_supported: true,
header_libs: [
+ "libbinder_headers_base",
+ "liblog_stub",
"trusty_mock_headers",
],
+ shared_libs: [
+ "libutils_binder_sdk",
+ ],
+
cflags: [
"-DBINDER_RPC_SINGLE_THREADED",
+ "-DBINDER_ENABLE_LIBLOG_ASSERT",
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ "-DBINDER_DISABLE_BLOB",
+ "-DBINDER_NO_LIBBASE",
+ // TODO: switch to "vendor: true" rather than copying this
// Trusty libbinder uses vendor stability for its binders
+ "-D__ANDROID_VENDOR__",
"-D__ANDROID_VNDK__",
"-U__ANDROID__",
"-D__TRUSTY__",
@@ -250,8 +290,6 @@
srcs: [
// Trusty-specific files
- "OS_android.cpp",
- "trusty/logging.cpp",
"trusty/OS.cpp",
"trusty/RpcServerTrusty.cpp",
"trusty/RpcTransportTipcTrusty.cpp",
@@ -346,6 +384,44 @@
afdo: true,
}
+cc_library_host_shared {
+ name: "libbinder_sdk",
+
+ defaults: [
+ "libbinder_common_defaults",
+ ],
+
+ shared_libs: [
+ "libutils_binder_sdk",
+ ],
+
+ cflags: [
+ "-DBINDER_ENABLE_LIBLOG_ASSERT",
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ "-DBINDER_DISABLE_BLOB",
+ "-DBINDER_NO_LIBBASE",
+ ],
+
+ header_libs: [
+ "liblog_stub",
+ ],
+
+ srcs: [
+ "OS_non_android_linux.cpp",
+ "OS_unix_base.cpp",
+ ],
+
+ visibility: [
+ ":__subpackages__",
+ ],
+
+ target: {
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
cc_library_static {
name: "libbinder_rpc_no_kernel",
vendor_available: true,
@@ -578,11 +654,6 @@
],
}
-filegroup {
- name: "libbinder_rpc_unstable_header",
- srcs: ["include_rpc_unstable/binder_rpc_unstable.hpp"],
-}
-
// libbinder historically contained additional interfaces that provided specific
// functionality in the platform but have nothing to do with binder itself. These
// are moved out of libbinder in order to avoid the overhead of their vtables.
@@ -651,4 +722,7 @@
"libutils",
"android.debug_aidl-cpp",
],
+ static_libs: [
+ "libc++fs",
+ ],
}
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index f22e90a..c57c9cd 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,8 +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>
#include <binder/IPCThreadState.h>
@@ -29,8 +27,8 @@
#include <binder/Parcel.h>
#include <binder/RecordedTransaction.h>
#include <binder/RpcServer.h>
+#include <binder/unique_fd.h>
#include <pthread.h>
-#include <utils/misc.h>
#include <inttypes.h>
#include <stdio.h>
@@ -45,6 +43,8 @@
namespace android {
+using android::binder::unique_fd;
+
constexpr uid_t kUidRoot = 0;
// Service implementations inherit from BBinder and IBinder, and this is frozen
@@ -169,8 +169,7 @@
return OK;
}
-status_t IBinder::setRpcClientDebug(android::base::unique_fd socketFd,
- const sp<IBinder>& keepAliveBinder) {
+status_t IBinder::setRpcClientDebug(unique_fd socketFd, const sp<IBinder>& keepAliveBinder) {
if (!kEnableRpcDevServers) {
ALOGW("setRpcClientDebug disallowed because RPC is not enabled");
return INVALID_OPERATION;
@@ -271,11 +270,11 @@
bool mInheritRt = false;
// for below objects
- Mutex mLock;
+ RpcMutex mLock;
std::set<sp<RpcServerLink>> mRpcServerLinks;
BpBinder::ObjectManager mObjects;
- android::base::unique_fd mRecordingFd;
+ unique_fd mRecordingFd;
};
// ---------------------------------------------------------------------------
@@ -307,9 +306,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 +316,7 @@
return readStatus;
}
mRecordingOn = true;
- LOG(INFO) << "Started Binder recording.";
+ ALOGI("Started Binder recording.");
return NO_ERROR;
}
}
@@ -337,14 +336,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 +377,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 +404,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 +414,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 +451,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 +460,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 +468,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 +476,7 @@
Extras* e = getOrCreateExtras();
LOG_ALWAYS_FATAL_IF(!e, "no memory");
- AutoMutex _l(e->mLock);
+ RpcMutexUniqueLock _l(e->mLock);
doWithLock();
}
@@ -485,7 +484,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);
}
@@ -642,7 +641,7 @@
}
status_t status;
bool hasSocketFd;
- android::base::unique_fd clientFd;
+ unique_fd clientFd;
if (status = data.readBool(&hasSocketFd); status != OK) return status;
if (hasSocketFd) {
@@ -654,8 +653,7 @@
return setRpcClientDebug(std::move(clientFd), keepAliveBinder);
}
-status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd,
- const sp<IBinder>& keepAliveBinder) {
+status_t BBinder::setRpcClientDebug(unique_fd socketFd, const sp<IBinder>& keepAliveBinder) {
if (!kEnableRpcDevServers) {
ALOGW("%s: disallowed because RPC is not enabled", __PRETTY_FUNCTION__);
return INVALID_OPERATION;
@@ -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..42dd691 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -23,22 +23,22 @@
#include <binder/IResultReceiver.h>
#include <binder/RpcSession.h>
#include <binder/Stability.h>
-#include <utils/Log.h>
#include <stdio.h>
#include "BuildFlags.h"
-
-#include <android-base/file.h>
+#include "file.h"
//#undef ALOGV
//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
namespace android {
+using android::binder::unique_fd;
+
// ---------------------------------------------------------------------------
-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 +163,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 +276,7 @@
}
bool BpBinder::isDescriptorCached() const {
- Mutex::Autolock _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
return mDescriptorCache.c_str() != kDescriptorUninit.c_str();
}
@@ -292,7 +292,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;
@@ -319,7 +319,7 @@
return transact(PING_TRANSACTION, data, &reply);
}
-status_t BpBinder::startRecordingBinder(const android::base::unique_fd& fd) {
+status_t BpBinder::startRecordingBinder(const unique_fd& fd) {
Parcel send, reply;
send.writeUniqueFileDescriptor(fd);
return transact(START_RECORDING_TRANSACTION, send, &reply);
@@ -385,7 +385,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 +431,7 @@
"linkToDeath(): recipient must be non-NULL");
{
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
if (!mObitsSent) {
if (!mObituaries) {
@@ -467,7 +467,7 @@
return INVALID_OPERATION;
}
- AutoMutex _l(mLock);
+ RpcMutexUniqueLock _l(mLock);
if (mObitsSent) {
return DEAD_OBJECT;
@@ -555,30 +555,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 +602,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 +702,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 +717,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 +731,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..455a433 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -21,16 +21,20 @@
#include <poll.h>
-#include <android-base/macros.h>
-#include <android-base/scopeguard.h>
+#include <binder/Functional.h>
+#include "FdUtils.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
- if (!android::base::Pipe(&ret->mRead, &ret->mWrite)) {
+ if (!binder::Pipe(&ret->mRead, &ret->mWrite)) {
ALOGE("Could not create pipe %s", strerror(errno));
return nullptr;
}
@@ -50,7 +54,7 @@
#ifdef BINDER_RPC_SINGLE_THREADED
return mTriggered;
#else
- return mWrite == -1;
+ return !mWrite.ok();
#endif
}
@@ -74,10 +78,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..e4a0283 100644
--- a/libs/binder/FdTrigger.h
+++ b/libs/binder/FdTrigger.h
@@ -17,11 +17,10 @@
#include <memory>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
#include <utils/Errors.h>
#include <binder/RpcTransport.h>
+#include <binder/unique_fd.h>
namespace android {
@@ -62,8 +61,8 @@
#ifdef BINDER_RPC_SINGLE_THREADED
bool mTriggered = false;
#else
- base::unique_fd mWrite;
- base::unique_fd mRead;
+ binder::unique_fd mWrite;
+ binder::unique_fd mRead;
#endif
};
} // namespace android
diff --git a/libs/binder/FdUtils.h b/libs/binder/FdUtils.h
new file mode 100644
index 0000000..52ae487
--- /dev/null
+++ b/libs/binder/FdUtils.h
@@ -0,0 +1,99 @@
+/*
+ * 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/unique_fd.h>
+
+#if defined(_WIN32) || defined(__TRUSTY__)
+// Pipe and Socketpair are missing there
+#elif !defined(BINDER_NO_LIBBASE)
+
+namespace android::binder {
+using android::base::Pipe;
+using android::base::Socketpair;
+} // namespace android::binder
+
+#else // BINDER_NO_LIBBASE
+
+#include <sys/socket.h>
+
+namespace android::binder {
+
+// Inline functions, so that they can be used header-only.
+
+// See pipe(2).
+// This helper hides the details of converting to unique_fd, and also hides the
+// fact that macOS doesn't support O_CLOEXEC or O_NONBLOCK directly.
+inline bool Pipe(unique_fd* read, unique_fd* write, int flags = O_CLOEXEC) {
+ int pipefd[2];
+
+#if defined(__APPLE__)
+ if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
+ return false;
+ }
+ if (pipe(pipefd) != 0) {
+ return false;
+ }
+
+ if (flags & O_CLOEXEC) {
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+ }
+ if (flags & O_NONBLOCK) {
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 ||
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+ }
+#else
+ if (pipe2(pipefd, flags) != 0) {
+ return false;
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+
+// See socketpair(2).
+// This helper hides the details of converting to unique_fd.
+inline bool Socketpair(int domain, int type, int protocol, unique_fd* left, unique_fd* right) {
+ int sockfd[2];
+ if (socketpair(domain, type, protocol, sockfd) != 0) {
+ return false;
+ }
+ left->reset(sockfd[0]);
+ right->reset(sockfd[1]);
+ return true;
+}
+
+// See socketpair(2).
+// This helper hides the details of converting to unique_fd.
+inline bool Socketpair(int type, unique_fd* left, unique_fd* right) {
+ return Socketpair(AF_UNIX, type, 0, left, right);
+}
+
+} // namespace android::binder
+
+#endif // BINDER_NO_LIBBASE
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..b92e504 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>
@@ -68,28 +67,28 @@
// Static const and functions will be optimized out if not used,
// when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
-static const char *kReturnStrings[] = {
- "BR_ERROR",
- "BR_OK",
- "BR_TRANSACTION",
- "BR_REPLY",
- "BR_ACQUIRE_RESULT",
- "BR_DEAD_REPLY",
- "BR_TRANSACTION_COMPLETE",
- "BR_INCREFS",
- "BR_ACQUIRE",
- "BR_RELEASE",
- "BR_DECREFS",
- "BR_ATTEMPT_ACQUIRE",
- "BR_NOOP",
- "BR_SPAWN_LOOPER",
- "BR_FINISHED",
- "BR_DEAD_BINDER",
- "BR_CLEAR_DEATH_NOTIFICATION_DONE",
- "BR_FAILED_REPLY",
- "BR_FROZEN_REPLY",
- "BR_ONEWAY_SPAM_SUSPECT",
- "BR_TRANSACTION_SEC_CTX",
+static const char* kReturnStrings[] = {
+ "BR_ERROR",
+ "BR_OK",
+ "BR_TRANSACTION/BR_TRANSACTION_SEC_CTX",
+ "BR_REPLY",
+ "BR_ACQUIRE_RESULT",
+ "BR_DEAD_REPLY",
+ "BR_TRANSACTION_COMPLETE",
+ "BR_INCREFS",
+ "BR_ACQUIRE",
+ "BR_RELEASE",
+ "BR_DECREFS",
+ "BR_ATTEMPT_ACQUIRE",
+ "BR_NOOP",
+ "BR_SPAWN_LOOPER",
+ "BR_FINISHED",
+ "BR_DEAD_BINDER",
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+ "BR_FAILED_REPLY",
+ "BR_FROZEN_REPLY",
+ "BR_ONEWAY_SPAM_SUSPECT",
+ "BR_TRANSACTION_PENDING_FROZEN",
};
static const char *kCommandStrings[] = {
@@ -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..0035aeb 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -18,14 +18,16 @@
#include <stddef.h>
#include <cstdint>
-#include <android-base/result.h>
-#include <android-base/unique_fd.h>
#include <binder/RpcTransport.h>
+#include <binder/unique_fd.h>
#include <utils/Errors.h>
namespace android::binder::os {
-android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd);
+void trace_begin(uint64_t tag, const char* name);
+void trace_end(uint64_t tag);
+
+status_t setNonBlocking(borrowed_fd fd);
status_t getRandomBytes(uint8_t* data, size_t size);
@@ -33,13 +35,11 @@
std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory();
-ssize_t sendMessageOnSocket(
- const RpcTransportFd& socket, iovec* iovs, int niovs,
- const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+ssize_t sendMessageOnSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds);
-ssize_t receiveMessageFromSocket(
- const RpcTransportFd& socket, iovec* iovs, int niovs,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+ssize_t receiveMessageFromSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds);
uint64_t GetThreadId();
diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp
index ad458eb..1eace85 100644
--- a/libs/binder/OS_android.cpp
+++ b/libs/binder/OS_android.cpp
@@ -17,9 +17,11 @@
#include "OS.h"
#include <android-base/threads.h>
+#include <cutils/trace.h>
#include <utils/misc.h>
-namespace android::binder::os {
+namespace android::binder {
+namespace os {
uint64_t GetThreadId() {
#ifdef BINDER_RPC_SINGLE_THREADED
@@ -34,4 +36,24 @@
return true;
}
-} // namespace android::binder::os
+void trace_begin(uint64_t tag, const char* name) {
+ atrace_begin(tag, name);
+}
+
+void trace_end(uint64_t tag) {
+ atrace_end(tag);
+}
+
+} // namespace os
+
+// Legacy trace symbol. To be removed once all of downstream rebuilds.
+void atrace_begin(uint64_t tag, const char* name) {
+ os::trace_begin(tag, name);
+}
+
+// Legacy trace symbol. To be removed once all of downstream rebuilds.
+void atrace_end(uint64_t tag) {
+ os::trace_end(tag);
+}
+
+} // namespace android::binder
diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp
new file mode 100644
index 0000000..b525d1a
--- /dev/null
+++ b/libs/binder/OS_non_android_linux.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "OS.h"
+
+#include <log/log.h>
+
+#include <syscall.h>
+#include <cstdarg>
+
+#ifdef __ANDROID__
+#error "This module is not intended for Android, just bare Linux"
+#endif
+#ifdef __APPLE__
+#error "This module is not intended for MacOS"
+#endif
+#ifdef _WIN32
+#error "This module is not intended for Windows"
+#endif
+
+namespace android::binder::os {
+
+void trace_begin(uint64_t, const char*) {}
+
+void trace_end(uint64_t) {}
+
+uint64_t GetThreadId() {
+ return syscall(__NR_gettid);
+}
+
+bool report_sysprop_change() {
+ return false;
+}
+
+} // namespace android::binder::os
+
+int __android_log_print(int /*prio*/, const char* /*tag*/, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ return 1;
+}
diff --git a/libs/binder/OS_unix_base.cpp b/libs/binder/OS_unix_base.cpp
index 81933d5..ca998d4 100644
--- a/libs/binder/OS_unix_base.cpp
+++ b/libs/binder/OS_unix_base.cpp
@@ -15,39 +15,41 @@
*/
#include "OS.h"
+#include "Utils.h"
+#include "file.h"
-#include <android-base/file.h>
#include <binder/RpcTransportRaw.h>
#include <log/log.h>
#include <string.h>
+#include <sys/socket.h>
-using android::base::ErrnoError;
-using android::base::Result;
+using android::binder::ReadFully;
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(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) {
- int ret = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
- if (ret == -1) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)));
+ if (!fd.ok()) {
return -errno;
}
- base::unique_fd fd(ret);
- if (!base::ReadFully(fd, data, size)) {
+ if (!ReadFully(fd, data, size)) {
return -errno;
}
return OK;
@@ -67,9 +69,8 @@
return RpcTransportCtxFactoryRaw::make();
}
-ssize_t sendMessageOnSocket(
- const RpcTransportFd& socket, iovec* iovs, int niovs,
- const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ssize_t sendMessageOnSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) {
if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
if (ancillaryFds->size() > kMaxFdsPerMsg) {
errno = EINVAL;
@@ -112,9 +113,8 @@
return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
}
-ssize_t receiveMessageFromSocket(
- const RpcTransportFd& socket, iovec* iovs, int niovs,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ssize_t receiveMessageFromSocket(const RpcTransportFd& socket, iovec* iovs, int niovs,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) {
if (ancillaryFds != nullptr) {
int fdBuffer[kMaxFdsPerMsg];
alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
@@ -140,7 +140,7 @@
size_t fdCount = dataLen / sizeof(int);
ancillaryFds->reserve(ancillaryFds->size() + fdCount);
for (size_t i = 0; i < fdCount; i++) {
- ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
+ ancillaryFds->emplace_back(unique_fd(fdBuffer[i]));
}
break;
}
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index bb17683..a70b558 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 32456
-maco@google.com
+
smoreland@google.com
-tkjos@google.com
+tkjos@google.com # kernel
+maco@google.com # historical
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index a3ff7d2..c1770b3 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,10 @@
namespace android {
+using namespace android::binder::impl;
+using binder::borrowed_fd;
+using binder::unique_fd;
+
// many things compile this into prebuilts on the stack
#ifdef __LP64__
static_assert(sizeof(Parcel) == 120);
@@ -112,7 +114,7 @@
constexpr size_t kMaxFds = 1024;
// Maximum size of a blob to transfer in-place.
-static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
+[[maybe_unused]] static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
#if defined(__BIONIC__)
static void FdTag(int fd, const void* old_addr, const void* new_addr) {
@@ -211,7 +213,7 @@
}
#endif // BINDER_WITH_KERNEL_IPC
-static int toRawFd(const std::variant<base::unique_fd, base::borrowed_fd>& v) {
+static int toRawFd(const std::variant<unique_fd, borrowed_fd>& v) {
return std::visit([](const auto& fd) { return fd.get(); }, v);
}
@@ -627,7 +629,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) {
@@ -667,7 +669,7 @@
if (status_t status = binder::os::dupFileDescriptor(oldFd, &newFd); status != OK) {
ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status));
}
- rpcFields->mFds->emplace_back(base::unique_fd(newFd));
+ rpcFields->mFds->emplace_back(unique_fd(newFd));
// Fixup the index in the data.
mDataPos = newDataPos + 4;
if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) {
@@ -884,6 +886,9 @@
}
#ifdef BINDER_WITH_KERNEL_IPC
+
+#if defined(__ANDROID__)
+
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#elif defined(__ANDROID_RECOVERY__)
@@ -891,6 +896,14 @@
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
+
+#else // ANDROID not defined
+
+// If kernel binder is used in new environments, we need to make sure it's separated
+// out and has a separate header.
+constexpr int32_t kHeader = B_PACK_CHARS('U', 'N', 'K', 'N');
+#endif
+
#endif // BINDER_WITH_KERNEL_IPC
// Write RPC headers. (previously just the interface token)
@@ -1247,9 +1260,16 @@
const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) { return writeData(val); }
status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) { return writeData(val); }
-status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) { return writeData(val); }
-status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) { return writeData(val); }
-status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) { return writeData(val); }
+status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<unique_fd>& val) {
+ return writeData(val);
+}
+status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<unique_fd>>& val) {
+ return writeData(val);
+}
+status_t Parcel::writeUniqueFileDescriptorVector(
+ const std::unique_ptr<std::vector<unique_fd>>& val) {
+ return writeData(val);
+}
status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); }
status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); }
@@ -1302,9 +1322,16 @@
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const { return readData(val); }
status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const { return readData(val); }
-status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const { return readData(val); }
-status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const { return readData(val); }
-status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const { return readData(val); }
+status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<unique_fd>>* val) const {
+ return readData(val);
+}
+status_t Parcel::readUniqueFileDescriptorVector(
+ std::unique_ptr<std::vector<unique_fd>>* val) const {
+ return readData(val);
+}
+status_t Parcel::readUniqueFileDescriptorVector(std::vector<unique_fd>* val) const {
+ return readData(val);
+}
status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const { return readData(val); }
status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const { return readData(val); }
@@ -1504,11 +1531,11 @@
status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) {
if (auto* rpcFields = maybeRpcFields()) {
- std::variant<base::unique_fd, base::borrowed_fd> fdVariant;
+ std::variant<unique_fd, borrowed_fd> fdVariant;
if (takeOwnership) {
- fdVariant = base::unique_fd(fd);
+ fdVariant = unique_fd(fd);
} else {
- fdVariant = base::borrowed_fd(fd);
+ fdVariant = borrowed_fd(fd);
}
if (!mAllowFds) {
return FDS_NOT_ALLOWED;
@@ -1587,7 +1614,7 @@
return err;
}
-status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) {
+status_t Parcel::writeUniqueFileDescriptor(const unique_fd& fd) {
return writeDupFileDescriptor(fd.get());
}
@@ -2390,8 +2417,7 @@
return fd;
}
-status_t Parcel::readUniqueFileDescriptor(base::unique_fd* val) const
-{
+status_t Parcel::readUniqueFileDescriptor(unique_fd* val) const {
int got = readFileDescriptor();
if (got == BAD_TYPE) {
@@ -2412,8 +2438,7 @@
return OK;
}
-status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const
-{
+status_t Parcel::readUniqueParcelFileDescriptor(unique_fd* val) const {
int got = readParcelFileDescriptor();
if (got == BAD_TYPE) {
@@ -2710,8 +2735,7 @@
status_t Parcel::rpcSetDataReference(
const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
const uint32_t* objectTable, size_t objectTableSize,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds,
- release_func relFunc) {
+ std::vector<std::variant<unique_fd, borrowed_fd>>&& ancillaryFds, release_func relFunc) {
// this code uses 'mOwner == nullptr' to understand whether it owns memory
LOG_ALWAYS_FATAL_IF(relFunc == nullptr, "must provide cleanup function");
diff --git a/libs/binder/ParcelFileDescriptor.cpp b/libs/binder/ParcelFileDescriptor.cpp
index 4f8b76f..c3c4874 100644
--- a/libs/binder/ParcelFileDescriptor.cpp
+++ b/libs/binder/ParcelFileDescriptor.cpp
@@ -19,9 +19,11 @@
namespace android {
namespace os {
+using android::binder::unique_fd;
+
ParcelFileDescriptor::ParcelFileDescriptor() = default;
-ParcelFileDescriptor::ParcelFileDescriptor(android::base::unique_fd fd) : mFd(std::move(fd)) {}
+ParcelFileDescriptor::ParcelFileDescriptor(unique_fd fd) : mFd(std::move(fd)) {}
ParcelFileDescriptor::~ParcelFileDescriptor() = default;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 8ec4af9..7de94e3 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,9 @@
namespace android {
+using namespace android::binder::impl;
+using android::binder::unique_fd;
+
class PoolThread : public Thread
{
public:
@@ -189,7 +192,7 @@
void ProcessState::startThreadPool()
{
- AutoMutex _l(mLock);
+ std::unique_lock<std::mutex> _l(mLock);
if (!mThreadPoolStarted) {
if (mMaxThreads == 0) {
// see also getThreadPoolMaxTotalThreadCount
@@ -203,7 +206,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 +313,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 +377,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 +433,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 +515,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 unique_fd open_driver(const char* driver) {
+ auto fd = 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 +564,27 @@
mThreadPoolStarted(false),
mThreadPoolSeq(1),
mCallRestriction(CallRestriction::NONE) {
- base::Result<int> opened = open_driver(driver);
+ 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..525ba2e 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -14,17 +14,23 @@
* limitations under the License.
*/
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/scopeguard.h>
-#include <android-base/unique_fd.h>
+#include "file.h"
+
+#include <binder/Functional.h>
#include <binder/RecordedTransaction.h>
+#include <binder/unique_fd.h>
+
+#include <inttypes.h>
#include <sys/mman.h>
+#include <sys/stat.h>
#include <algorithm>
+using namespace android::binder::impl;
using android::Parcel;
-using android::base::borrowed_fd;
-using android::base::unique_fd;
+using android::binder::borrowed_fd;
+using android::binder::ReadFully;
+using android::binder::unique_fd;
+using android::binder::WriteFully;
using android::binder::debug::RecordedTransaction;
#define PADDING8(s) ((8 - (s) % 8) % 8)
@@ -126,18 +132,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 +172,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);
+ if (!ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
+ 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 +210,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 +231,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 +239,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 +267,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 +275,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 +283,7 @@
case END_CHUNK:
break;
default:
- LOG(INFO) << "Unrecognized chunk.";
+ ALOGI("Unrecognized chunk.");
break;
}
} while (chunk.chunkType != END_CHUNK);
@@ -291,7 +294,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,
@@ -319,8 +322,8 @@
buffer.insert(buffer.end(), checksumBytes, checksumBytes + sizeof(transaction_checksum_t));
// Write buffer to file
- if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
- LOG(ERROR) << "Failed to write chunk fd " << fd.get();
+ if (!WriteFully(fd, buffer.data(), buffer.size())) {
+ ALOGE("Failed to write chunk fd %d", fd.get());
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -330,26 +333,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..d9e926a 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,8 +44,9 @@
constexpr size_t kSessionIdBytes = 32;
-using base::ScopeGuard;
-using base::unique_fd;
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {}
RpcServer::~RpcServer() {
@@ -167,9 +167,9 @@
mConnectionFilter = std::move(filter);
}
-void RpcServer::setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier) {
+void RpcServer::setServerSocketModifier(std::function<void(borrowed_fd)>&& modifier) {
RpcMutexLockGuard _l(mLock);
- LOG_ALWAYS_FATAL_IF(mServer.fd != -1, "Already started");
+ LOG_ALWAYS_FATAL_IF(mServer.fd.ok(), "Already started");
mServerSocketModifier = std::move(modifier);
}
@@ -201,7 +201,7 @@
status_t RpcServer::acceptSocketConnection(const RpcServer& server, RpcTransportFd* out) {
RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
accept4(server.mServer.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK))));
- if (clientSocket.fd < 0) {
+ if (!clientSocket.fd.ok()) {
int savedErrno = errno;
ALOGE("Could not accept4 socket: %s", strerror(savedErrno));
return -savedErrno;
@@ -214,7 +214,7 @@
status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out) {
int zero = 0;
iovec iov{&zero, sizeof(zero)};
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ std::vector<std::variant<unique_fd, borrowed_fd>> fds;
ssize_t num_bytes = binder::os::receiveMessageFromSocket(server.mServer, &iov, 1, &fds);
if (num_bytes < 0) {
@@ -231,10 +231,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 +455,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 +546,7 @@
return;
}
- detachGuard.Disable();
+ detachGuard.release();
session->preJoinThreadOwnership(std::move(thisThread));
}
@@ -647,8 +645,7 @@
}
status_t RpcServer::setupExternalServer(
- base::unique_fd serverFd,
- std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn) {
+ unique_fd serverFd, std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn) {
RpcMutexLockGuard _l(mLock);
if (mServer.fd.ok()) {
ALOGE("Each RpcServer can only have one server.");
@@ -659,7 +656,7 @@
return OK;
}
-status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+status_t RpcServer::setupExternalServer(unique_fd serverFd) {
return setupExternalServer(std::move(serverFd), &RpcServer::acceptSocketConnection);
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index fa8f2b5..16a7f9f 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,7 +50,9 @@
namespace android {
-using base::unique_fd;
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {
LOG_RPC_DETAIL("RpcSession created %p", this);
@@ -158,7 +158,7 @@
int zero = 0;
iovec iov{&zero, sizeof(zero)};
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ std::vector<std::variant<unique_fd, borrowed_fd>> fds;
fds.push_back(std::move(serverFd));
status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov,
@@ -187,17 +187,13 @@
return NAME_NOT_FOUND;
}
-status_t RpcSession::setupPreconnectedClient(base::unique_fd fd,
- std::function<unique_fd()>&& request) {
+status_t RpcSession::setupPreconnectedClient(unique_fd fd, std::function<unique_fd()>&& request) {
return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) -> status_t {
if (!fd.ok()) {
fd = request();
if (!fd.ok()) return BAD_VALUE;
}
- if (auto res = binder::os::setNonBlocking(fd); !res.ok()) {
- ALOGE("setupPreconnectedClient: %s", res.error().message().c_str());
- return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
- }
+ if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res;
RpcTransportFd transportFd(std::move(fd));
status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming);
@@ -212,7 +208,7 @@
unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
- if (serverFd == -1) {
+ if (!serverFd.ok()) {
int savedErrno = errno;
ALOGE("Could not connect to /dev/null: %s", strerror(savedErrno));
return -savedErrno;
@@ -412,7 +408,9 @@
}
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;
}
@@ -596,7 +594,7 @@
unique_fd serverFd(TEMP_FAILURE_RETRY(
socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
- if (serverFd == -1) {
+ if (!serverFd.ok()) {
int savedErrno = errno;
ALOGE("Could not create socket at %s: %s", addr.toString().c_str(),
strerror(savedErrno));
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 5046253..fe6e1a3 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,9 @@
namespace android {
-using base::StringPrintf;
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
#if RPC_FLAKE_PRONE
void rpcMaybeWaitToFlake() {
@@ -329,8 +330,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) {
@@ -354,11 +357,10 @@
mData.reset(new (std::nothrow) uint8_t[size]);
}
-status_t RpcState::rpcSend(
- const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
- const char* what, iovec* iovs, int niovs,
- const std::optional<android::base::function_ref<status_t()>>& altPoll,
- const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+status_t RpcState::rpcSend(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) {
for (int i = 0; i < niovs; i++) {
LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s",
what, i + 1, niovs, connection->rpcTransport.get(),
@@ -379,10 +381,9 @@
return OK;
}
-status_t RpcState::rpcRec(
- const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
- const char* what, iovec* iovs, int niovs,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+status_t RpcState::rpcRec(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) {
if (status_t status =
connection->rpcTransport->interruptableReadFully(session->mShutdownTrigger.get(),
iovs, niovs, std::nullopt,
@@ -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
@@ -651,7 +649,7 @@
status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply) {
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
+ std::vector<std::variant<unique_fd, borrowed_fd>> ancillaryFds;
RpcWireHeader command;
while (true) {
iovec iov{&command, sizeof(command)};
@@ -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,14 +760,14 @@
.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,
const sp<RpcSession>& session, CommandType type) {
LOG_RPC_DETAIL("getAndExecuteCommand on RpcTransport %p", connection->rpcTransport.get());
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
+ std::vector<std::variant<unique_fd, borrowed_fd>> ancillaryFds;
RpcWireHeader command;
iovec iov{&command, sizeof(command)};
if (status_t status =
@@ -798,7 +796,7 @@
status_t RpcState::processCommand(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const RpcWireHeader& command, CommandType type,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
+ std::vector<std::variant<unique_fd, borrowed_fd>>&& ancillaryFds) {
#ifdef BINDER_WITH_KERNEL_IPC
IPCThreadState* kernelBinderState = IPCThreadState::selfOrNull();
IPCThreadState::SpGuard spGuard{
@@ -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) {
@@ -838,7 +836,7 @@
status_t RpcState::processTransact(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const RpcWireHeader& command,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
+ std::vector<std::variant<unique_fd, borrowed_fd>>&& ancillaryFds) {
LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);
CommandData transactionData(command.bodySize);
@@ -865,7 +863,7 @@
status_t RpcState::processTransactInternal(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
CommandData transactionData,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds) {
+ std::vector<std::variant<unique_fd, borrowed_fd>>&& ancillaryFds) {
// for 'recursive' calls to this, we have already read and processed the
// binder from the transaction data and taken reference counts into account,
// so it is cached here.
@@ -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..8b84602 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -15,11 +15,12 @@
*/
#pragma once
-#include <android-base/unique_fd.h>
+#include <binder/Functional.h>
#include <binder/IBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
#include <binder/RpcThreads.h>
+#include <binder/unique_fd.h>
#include <map>
#include <optional>
@@ -190,28 +191,29 @@
[[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::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds =
+ const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>* ancillaryFds =
nullptr);
- [[nodiscard]] status_t rpcRec(
- const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
- const char* what, iovec* iovs, int niovs,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr);
+ [[nodiscard]] status_t rpcRec(const sp<RpcSession::RpcConnection>& connection,
+ const sp<RpcSession>& session, const char* what, iovec* iovs,
+ int niovs,
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>*
+ ancillaryFds = nullptr);
[[nodiscard]] status_t waitForReply(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session, Parcel* reply);
[[nodiscard]] status_t processCommand(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const RpcWireHeader& command, CommandType type,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds);
[[nodiscard]] status_t processTransact(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
const RpcWireHeader& command,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds);
[[nodiscard]] status_t processTransactInternal(
const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session,
CommandData transactionData,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds);
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds);
[[nodiscard]] status_t processDecStrong(const sp<RpcSession::RpcConnection>& connection,
const sp<RpcSession>& session,
const RpcWireHeader& command);
@@ -253,7 +255,7 @@
struct AsyncTodo {
sp<IBinder> ref;
CommandData data;
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> ancillaryFds;
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> ancillaryFds;
uint64_t asyncNumber = 0;
bool operator<(const AsyncTodo& o) const {
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index c089811..aa3a6e5 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -19,6 +19,7 @@
#include <poll.h>
#include <stddef.h>
+#include <sys/socket.h>
#include <binder/RpcTransportRaw.h>
@@ -29,6 +30,10 @@
namespace android {
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
+
// RpcTransport with TLS disabled.
class RpcTransportRaw : public RpcTransport {
public:
@@ -54,9 +59,8 @@
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)
- override {
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
bool sentFds = false;
auto send = [&](iovec* iovs, int niovs) -> ssize_t {
ssize_t ret = binder::os::sendMessageOnSocket(mSocket, iovs, niovs,
@@ -70,8 +74,8 @@
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) override {
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ std::vector<std::variant<unique_fd, 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..3819fb6 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -26,8 +26,9 @@
#include "RpcState.h"
#include "RpcTransportUtils.h"
-using android::base::Error;
-using android::base::Result;
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
namespace android {
@@ -75,9 +76,8 @@
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)
- override {
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
// TODO: send ancillaryFds. For now, we just abort if anyone tries
// to send any.
@@ -93,9 +93,8 @@
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*/)
- override {
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* /*ancillaryFds*/) override {
auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t {
// Fill the read buffer at most once per readFn call, then try to
// return as much of it as possible. If the input iovecs are spread
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index efb09e9..579694c 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -18,6 +18,7 @@
#include <log/log.h>
#include <poll.h>
+#include <sys/socket.h>
#include <openssl/bn.h>
#include <openssl/ssl.h>
@@ -29,6 +30,8 @@
#include "RpcState.h"
#include "Utils.h"
+#include <sstream>
+
#define SHOULD_LOG_TLS_DETAIL false
#if SHOULD_LOG_TLS_DETAIL
@@ -38,6 +41,11 @@
#endif
namespace android {
+
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
+
namespace {
// Implement BIO for socket that ignores SIGPIPE.
@@ -51,7 +59,7 @@
return 1;
}
int socketRead(BIO* bio, char* buf, int size) {
- android::base::borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
+ borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
int ret = TEMP_FAILURE_RETRY(::recv(fd.get(), buf, size, MSG_NOSIGNAL));
BIO_clear_retry_flags(bio);
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -61,7 +69,7 @@
}
int socketWrite(BIO* bio, const char* buf, int size) {
- android::base::borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
+ borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
int ret = TEMP_FAILURE_RETRY(::send(fd.get(), buf, size, MSG_NOSIGNAL));
BIO_clear_retry_flags(bio);
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -71,13 +79,13 @@
}
long socketCtrl(BIO* bio, int cmd, long num, void*) { // NOLINT
- android::base::borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
+ borrowed_fd fd(static_cast<int>(reinterpret_cast<intptr_t>(BIO_get_data(bio))));
if (cmd == BIO_CTRL_FLUSH) return 1;
LOG_ALWAYS_FATAL("sockCtrl(fd=%d, %d, %ld)", fd.get(), cmd, num);
return 0;
}
-bssl::UniquePtr<BIO> newSocketBio(android::base::borrowed_fd fd) {
+bssl::UniquePtr<BIO> newSocketBio(borrowed_fd fd) {
static const BIO_METHOD* gMethods = ([] {
auto methods = BIO_meth_new(BIO_get_new_index(), "socket_no_signal");
LOG_ALWAYS_FATAL_IF(0 == BIO_meth_set_write(methods, socketWrite), "BIO_meth_set_write");
@@ -181,10 +189,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 +207,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,13 +291,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::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
- override;
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override;
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) override;
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override;
bool isWaiting() override { return mSocket.isInPollingState(); };
@@ -320,8 +326,8 @@
status_t RpcTransportTls::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) {
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) {
(void)ancillaryFds;
MAYBE_WAIT_IN_FLAKE_MODE;
@@ -366,8 +372,8 @@
status_t RpcTransportTls::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) {
+ const std::optional<SmallFunction<status_t()>>& altPoll,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) {
(void)ancillaryFds;
MAYBE_WAIT_IN_FLAKE_MODE;
diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h
index 32f0db8..fcf6675 100644
--- a/libs/binder/RpcTransportUtils.h
+++ b/libs/binder/RpcTransportUtils.h
@@ -15,7 +15,7 @@
*/
#pragma once
-#include <android-base/unique_fd.h>
+#include <binder/unique_fd.h>
#include <poll.h>
#include "FdTrigger.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..a445196 100644
--- a/libs/binder/RpcTrusty.cpp
+++ b/libs/binder/RpcTrusty.cpp
@@ -16,15 +16,14 @@
#define LOG_TAG "RpcTrusty"
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
#include <binder/RpcSession.h>
#include <binder/RpcTransportTipcAndroid.h>
+#include <binder/unique_fd.h>
#include <trusty/tipc.h>
namespace android {
-using android::base::unique_fd;
+using android::binder::unique_fd;
sp<RpcSession> RpcTrustyConnectWithSessionInitializer(
const char* device, const char* port,
@@ -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/Stability.cpp b/libs/binder/Stability.cpp
index c432b3a..665dfea 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -73,6 +73,14 @@
(void)setRepr(binder, getLocalLevel(), REPR_NONE);
}
+// after deprecation of the VNDK, these should be aliases. At some point
+// all references to __ANDROID_VNDK__ should be replaced by __ANDROID_VENDOR__
+// but for right now, check that this condition holds because some
+// places check __ANDROID_VNDK__ and some places check __ANDROID_VENDOR__
+#if defined(__ANDROID_VNDK__) != defined(__ANDROID_VENDOR__)
+#error "__ANDROID_VNDK__ and __ANDROID_VENDOR__ should be aliases"
+#endif
+
Stability::Level Stability::getLocalLevel() {
#ifdef __ANDROID_APEX__
#error "APEX can't use libbinder (must use libbinder_ndk)"
diff --git a/libs/binder/Trace.cpp b/libs/binder/Trace.cpp
deleted file mode 100644
index 1ebfa1a..0000000
--- a/libs/binder/Trace.cpp
+++ /dev/null
@@ -1,32 +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.
- */
-
-#include <binder/Trace.h>
-#include <cutils/trace.h>
-
-namespace android {
-namespace binder {
-
-void atrace_begin(uint64_t tag, const char* name) {
- ::atrace_begin(tag, name);
-}
-
-void atrace_end(uint64_t tag) {
- ::atrace_end(tag);
-}
-
-} // namespace binder
-} // 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..ae1a6c4 100644
--- a/libs/binder/UtilsHost.cpp
+++ b/libs/binder/UtilsHost.cpp
@@ -25,8 +25,13 @@
#include <log/log.h>
+#include "FdUtils.h"
+#include "Utils.h"
+
namespace android {
+using android::binder::unique_fd;
+
CommandResult::~CommandResult() {
if (!pid.has_value()) return;
if (*pid == 0) {
@@ -72,8 +77,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);
@@ -81,15 +86,22 @@
argv.push_back(nullptr);
CommandResult ret;
- android::base::unique_fd outWrite;
- if (!android::base::Pipe(&ret.outPipe, &outWrite))
- return android::base::ErrnoError() << "pipe() for outPipe";
- android::base::unique_fd errWrite;
- if (!android::base::Pipe(&ret.errPipe, &errWrite))
- return android::base::ErrnoError() << "pipe() for errPipe";
+ unique_fd outWrite;
+ if (!binder::Pipe(&ret.outPipe, &outWrite)) {
+ PLOGE("pipe() for outPipe");
+ return {};
+ }
+ unique_fd errWrite;
+ if (!binder::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();
@@ -111,7 +123,7 @@
errWrite.reset();
ret.pid = pid;
- auto handlePoll = [](android::base::unique_fd* fd, const pollfd* pfd, std::string* s) {
+ auto handlePoll = [](unique_fd* fd, const pollfd* pfd, std::string* s) {
if (!fd->ok()) return true;
if (pfd->revents & POLLIN) {
char buf[1024];
@@ -140,12 +152,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 +173,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..b582f17 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 <binder/unique_fd.h>
+#include <utils/Errors.h>
/**
* Log a lot more information about host-device binder communication, when debugging issues.
@@ -46,8 +46,8 @@
std::string stdoutStr;
std::string stderrStr;
- android::base::unique_fd outPipe;
- android::base::unique_fd errPipe;
+ binder::unique_fd outPipe;
+ binder::unique_fd errPipe;
CommandResult() = default;
CommandResult(CommandResult&& other) noexcept { (*this) = std::move(other); }
@@ -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/file.cpp b/libs/binder/file.cpp
new file mode 100644
index 0000000..6e450b9
--- /dev/null
+++ b/libs/binder/file.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "file.h"
+
+#ifdef BINDER_NO_LIBBASE
+
+#include "Utils.h"
+
+#include <stdint.h>
+
+// clang-format off
+
+namespace android::binder {
+
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
+ uint8_t* p = reinterpret_cast<uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
+ if (n == 0) { // EOF
+ errno = ENODATA;
+ return false;
+ }
+ if (n == -1) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
+ size_t remaining = byte_count;
+ while (remaining > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
+ if (n == -1) return false;
+ p += n;
+ remaining -= n;
+ }
+ return true;
+}
+
+} // namespace android::binder
+
+#endif // BINDER_NO_LIBBASE
diff --git a/libs/binder/file.h b/libs/binder/file.h
new file mode 100644
index 0000000..bcbfa31
--- /dev/null
+++ b/libs/binder/file.h
@@ -0,0 +1,41 @@
+/*
+ * 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
+
+#ifndef BINDER_NO_LIBBASE
+
+#include <android-base/file.h>
+
+namespace android::binder {
+using android::base::ReadFully;
+using android::base::WriteFully;
+} // namespace android::binder
+
+#else // BINDER_NO_LIBBASE
+
+#include <binder/unique_fd.h>
+
+#include <string_view>
+
+namespace android::binder {
+
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
+
+} // namespace android::binder
+
+#endif // BINDER_NO_LIBBASE
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 744da0f..7a65ff4 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -102,7 +102,7 @@
// to another process.
void setParceled();
- [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd clientFd,
+ [[nodiscard]] status_t setRpcClientDebug(binder::unique_fd clientFd,
const sp<IBinder>& keepAliveBinder);
protected:
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 28fb9f1..89a4d27 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -16,9 +16,9 @@
#pragma once
-#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
-#include <utils/Mutex.h>
+#include <binder/RpcThreads.h>
+#include <binder/unique_fd.h>
#include <map>
#include <optional>
@@ -94,7 +94,7 @@
// Start recording transactions to the unique_fd.
// See RecordedTransaction.h for more details.
- status_t startRecordingBinder(const android::base::unique_fd& fd);
+ status_t startRecordingBinder(const binder::unique_fd& fd);
// Stop the current recording.
status_t stopRecordingBinder();
@@ -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/Delegate.h b/libs/binder/include/binder/Delegate.h
index 8b3fc1c..ad5a6a3 100644
--- a/libs/binder/include/binder/Delegate.h
+++ b/libs/binder/include/binder/Delegate.h
@@ -18,6 +18,11 @@
#include <binder/IBinder.h>
+#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
+#include <log/log.h>
+#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message)
+#endif
+
#ifndef __BIONIC__
#ifndef __assert
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/IBinder.h b/libs/binder/include/binder/IBinder.h
index e75d548..dad9a17 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android-base/unique_fd.h>
+#include <binder/unique_fd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
@@ -175,7 +175,7 @@
*
* On death of @a keepAliveBinder, the RpcServer shuts down.
*/
- [[nodiscard]] status_t setRpcClientDebug(android::base::unique_fd socketFd,
+ [[nodiscard]] status_t setRpcClientDebug(binder::unique_fd socketFd,
const sp<IBinder>& keepAliveBinder);
// NOLINTNEXTLINE(google-default-arguments)
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index dc572ac..ac845bc 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -119,8 +119,8 @@
"The preferred way to add interfaces is to define " \
"an .aidl file to auto-generate the interface. If " \
"an interface must be manually written, add its " \
- "name to the whitelist."); \
- DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ "name to the allowlist."); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)
#else
@@ -305,10 +305,10 @@
return equals(a + 1, b + 1);
}
-constexpr bool inList(const char* a, const char* const* whitelist) {
- if (*whitelist == nullptr) return false;
- if (equals(a, *whitelist)) return true;
- return inList(a, whitelist + 1);
+constexpr bool inList(const char* a, const char* const* allowlist) {
+ if (*allowlist == nullptr) return false;
+ if (equals(a, *allowlist)) return true;
+ return inList(a, allowlist + 1);
}
constexpr bool allowedManualInterface(const char* name) {
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 9347ce4..dc5b1a1 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -62,8 +62,13 @@
/**
* Returns the PID of the process which has made the current binder
- * call. If not in a binder call, this will return getpid. If the
- * call is oneway, this will return 0.
+ * call. If not in a binder call, this will return getpid.
+ *
+ * Warning: oneway transactions do not receive PID. Even if you expect
+ * a transaction to be synchronous, a misbehaving client could send it
+ * as an asynchronous call and result in a 0 PID here. Additionally, if
+ * there is a race and the calling process dies, the PID may still be
+ * 0 for a synchronous call.
*/
[[nodiscard]] pid_t getCallingPid() const;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 98d12bb..09da6e3 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -25,7 +25,7 @@
#include <variant>
#include <vector>
-#include <android-base/unique_fd.h>
+#include <binder/unique_fd.h>
#ifndef BINDER_DISABLE_NATIVE_HANDLE
#include <cutils/native_handle.h>
#endif
@@ -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>
@@ -355,17 +354,16 @@
// Place a file descriptor into the parcel. This will not affect the
// semantics of the smart file descriptor. A new descriptor will be
// created, and will be closed when the parcel is destroyed.
- status_t writeUniqueFileDescriptor(
- const base::unique_fd& fd);
+ status_t writeUniqueFileDescriptor(const binder::unique_fd& fd);
// Place a vector of file desciptors into the parcel. Each descriptor is
// dup'd as in writeDupFileDescriptor
- status_t writeUniqueFileDescriptorVector(
- const std::optional<std::vector<base::unique_fd>>& val);
- status_t writeUniqueFileDescriptorVector(
- const std::unique_ptr<std::vector<base::unique_fd>>& val) __attribute__((deprecated("use std::optional version instead")));
- status_t writeUniqueFileDescriptorVector(
- const std::vector<base::unique_fd>& val);
+ status_t writeUniqueFileDescriptorVector(
+ const std::optional<std::vector<binder::unique_fd>>& val);
+ status_t writeUniqueFileDescriptorVector(
+ const std::unique_ptr<std::vector<binder::unique_fd>>& val)
+ __attribute__((deprecated("use std::optional version instead")));
+ status_t writeUniqueFileDescriptorVector(const std::vector<binder::unique_fd>& val);
// Writes a blob to the parcel.
// If the blob is small, then it is stored in-place, otherwise it is
@@ -580,20 +578,17 @@
int readParcelFileDescriptor() const;
// Retrieve a smart file descriptor from the parcel.
- status_t readUniqueFileDescriptor(
- base::unique_fd* val) const;
+ status_t readUniqueFileDescriptor(binder::unique_fd* val) const;
// Retrieve a Java "parcel file descriptor" from the parcel.
- status_t readUniqueParcelFileDescriptor(base::unique_fd* val) const;
-
+ status_t readUniqueParcelFileDescriptor(binder::unique_fd* val) const;
// Retrieve a vector of smart file descriptors from the parcel.
- status_t readUniqueFileDescriptorVector(
- std::optional<std::vector<base::unique_fd>>* val) const;
- status_t readUniqueFileDescriptorVector(
- std::unique_ptr<std::vector<base::unique_fd>>* val) const __attribute__((deprecated("use std::optional version instead")));
- status_t readUniqueFileDescriptorVector(
- std::vector<base::unique_fd>* val) const;
+ status_t readUniqueFileDescriptorVector(
+ std::optional<std::vector<binder::unique_fd>>* val) const;
+ status_t readUniqueFileDescriptorVector(std::unique_ptr<std::vector<binder::unique_fd>>* val)
+ const __attribute__((deprecated("use std::optional version instead")));
+ status_t readUniqueFileDescriptorVector(std::vector<binder::unique_fd>* val) const;
// Reads a blob from the parcel.
// The caller should call release() on the blob after reading its contents.
@@ -630,7 +625,7 @@
status_t rpcSetDataReference(
const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
const uint32_t* objectTable, size_t objectTableSize,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>&& ancillaryFds,
+ std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds,
release_func relFunc);
status_t finishWrite(size_t len);
@@ -707,7 +702,7 @@
// 5) Nullable objects contained in std::optional, std::unique_ptr, or std::shared_ptr.
//
// And active objects from the Android ecosystem such as:
- // 6) File descriptors, base::unique_fd (kernel object handles)
+ // 6) File descriptors, unique_fd (kernel object handles)
// 7) Binder objects, sp<IBinder> (active Android RPC handles)
//
// Objects from (1) through (5) serialize into the mData buffer.
@@ -958,9 +953,7 @@
return writeUtf8AsUtf16(t);
}
- status_t writeData(const base::unique_fd& t) {
- return writeUniqueFileDescriptor(t);
- }
+ status_t writeData(const binder::unique_fd& t) { return writeUniqueFileDescriptor(t); }
status_t writeData(const Parcelable& t) { // std::is_base_of_v<Parcelable, T>
// implemented here. writeParcelable() calls this.
@@ -1107,9 +1100,7 @@
return readUtf8FromUtf16(t);
}
- status_t readData(base::unique_fd* t) const {
- return readUniqueFileDescriptor(t);
- }
+ status_t readData(binder::unique_fd* t) const { return readUniqueFileDescriptor(t); }
status_t readData(Parcelable* t) const { // std::is_base_of_v<Parcelable, T>
// implemented here. readParcelable() calls this.
@@ -1329,7 +1320,7 @@
// same order as `mObjectPositions`.
//
// Boxed to save space. Lazy allocated.
- std::unique_ptr<std::vector<std::variant<base::unique_fd, base::borrowed_fd>>> mFds;
+ std::unique_ptr<std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>> mFds;
};
std::variant<KernelFields, RpcFields> mVariantFields;
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
index 08d8e43..c4ef354 100644
--- a/libs/binder/include/binder/ParcelFileDescriptor.h
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -16,9 +16,9 @@
#pragma once
-#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include <binder/unique_fd.h>
namespace android {
namespace os {
@@ -29,14 +29,14 @@
class ParcelFileDescriptor : public android::Parcelable {
public:
ParcelFileDescriptor();
- explicit ParcelFileDescriptor(android::base::unique_fd fd);
+ explicit ParcelFileDescriptor(binder::unique_fd fd);
ParcelFileDescriptor(ParcelFileDescriptor&& other) noexcept : mFd(std::move(other.mFd)) { }
ParcelFileDescriptor& operator=(ParcelFileDescriptor&& other) noexcept = default;
~ParcelFileDescriptor() override;
int get() const { return mFd.get(); }
- android::base::unique_fd release() { return std::move(mFd); }
- void reset(android::base::unique_fd fd = android::base::unique_fd()) { mFd = std::move(fd); }
+ binder::unique_fd release() { return std::move(mFd); }
+ void reset(binder::unique_fd fd = binder::unique_fd()) { mFd = std::move(fd); }
// android::Parcelable override:
android::status_t writeToParcel(android::Parcel* parcel) const override;
@@ -62,7 +62,7 @@
return mFd.get() >= rhs.mFd.get();
}
private:
- android::base::unique_fd mFd;
+ binder::unique_fd mFd;
};
} // namespace os
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/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h
index eb765fe..505c199 100644
--- a/libs/binder/include/binder/RecordedTransaction.h
+++ b/libs/binder/include/binder/RecordedTransaction.h
@@ -16,8 +16,8 @@
#pragma once
-#include <android-base/unique_fd.h>
#include <binder/Parcel.h>
+#include <binder/unique_fd.h>
#include <mutex>
namespace android {
@@ -31,7 +31,8 @@
class RecordedTransaction {
public:
// Filled with the first transaction from fd.
- static std::optional<RecordedTransaction> fromFile(const android::base::unique_fd& fd);
+
+ static std::optional<RecordedTransaction> fromFile(const binder::unique_fd& fd);
// Filled with the arguments.
static std::optional<RecordedTransaction> fromDetails(const String16& interfaceName,
uint32_t code, uint32_t flags,
@@ -39,7 +40,7 @@
const Parcel& reply, status_t err);
RecordedTransaction(RecordedTransaction&& t) noexcept;
- [[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const;
+ [[nodiscard]] status_t dumpToFile(const binder::unique_fd& fd) const;
const std::string& getInterfaceName() const;
uint32_t getCode() const;
@@ -53,8 +54,8 @@
private:
RecordedTransaction() = default;
- android::status_t writeChunk(const android::base::borrowed_fd, uint32_t chunkType,
- size_t byteCount, const uint8_t* data) const;
+ android::status_t writeChunk(const binder::borrowed_fd, uint32_t chunkType, size_t byteCount,
+ const uint8_t* data) const;
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wpadded"
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 2153f16..a07880d 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -15,11 +15,11 @@
*/
#pragma once
-#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcSession.h>
#include <binder/RpcThreads.h>
#include <binder/RpcTransport.h>
+#include <binder/unique_fd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -59,7 +59,7 @@
* to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client
* session can be created from the client end of the pair.
*/
- [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(base::unique_fd serverFd);
+ [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(binder::unique_fd serverFd);
/**
* This represents a session for responses, e.g.:
@@ -79,7 +79,7 @@
* This method is used in the libbinder_rpc_unstable API
* RunInitUnixDomainRpcServer().
*/
- [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);
+ [[nodiscard]] status_t setupRawSocketServer(binder::unique_fd socket_fd);
/**
* Creates an RPC server binding to the given CID at the given port.
@@ -111,13 +111,13 @@
/**
* If hasServer(), return the server FD. Otherwise return invalid FD.
*/
- [[nodiscard]] base::unique_fd releaseServer();
+ [[nodiscard]] binder::unique_fd releaseServer();
/**
* Set up server using an external FD previously set up by releaseServer().
* Return false if there's already a server.
*/
- [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
+ [[nodiscard]] status_t setupExternalServer(binder::unique_fd serverFd);
/**
* This must be called before adding a client session. This corresponds
@@ -193,7 +193,7 @@
*
* The only argument is a successfully created file descriptor, not bound to an address yet.
*/
- void setServerSocketModifier(std::function<void(base::borrowed_fd)>&& modifier);
+ void setServerSocketModifier(std::function<void(binder::borrowed_fd)>&& modifier);
/**
* See RpcTransportCtx::getCertificate
@@ -249,7 +249,7 @@
void onSessionIncomingThreadEnded() override;
status_t setupExternalServer(
- base::unique_fd serverFd,
+ binder::unique_fd serverFd,
std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn);
static constexpr size_t kRpcAddressSize = 128;
@@ -279,7 +279,7 @@
wp<IBinder> mRootObjectWeak;
std::function<sp<IBinder>(wp<RpcSession>, const void*, size_t)> mRootObjectFactory;
std::function<bool(const void*, size_t)> mConnectionFilter;
- std::function<void(base::borrowed_fd)> mServerSocketModifier;
+ std::function<void(binder::borrowed_fd)> mServerSocketModifier;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
RpcConditionVariable mShutdownCv;
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index cb64603..11fbde9 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -15,11 +15,10 @@
*/
#pragma once
-#include <android-base/threads.h>
-#include <android-base/unique_fd.h>
#include <binder/IBinder.h>
#include <binder/RpcThreads.h>
#include <binder/RpcTransport.h>
+#include <binder/unique_fd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -124,7 +123,7 @@
/**
* Connects to an RPC server over a nameless Unix domain socket pair.
*/
- [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(base::unique_fd bootstrap);
+ [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(binder::unique_fd bootstrap);
/**
* Connects to an RPC server at the CVD & port.
@@ -146,8 +145,8 @@
*
* For future compatibility, 'request' should not reference any stack data.
*/
- [[nodiscard]] status_t setupPreconnectedClient(base::unique_fd fd,
- std::function<base::unique_fd()>&& request);
+ [[nodiscard]] status_t setupPreconnectedClient(binder::unique_fd fd,
+ std::function<binder::unique_fd()>&& request);
/**
* For debugging!
diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h
index 6db9ad9..a50cdc1 100644
--- a/libs/binder/include/binder/RpcTransport.h
+++ b/libs/binder/include/binder/RpcTransport.h
@@ -25,12 +25,12 @@
#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>
+#include <binder/unique_fd.h>
#include <sys/uio.h>
@@ -85,13 +85,14 @@
* 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<binder::unique_fd, binder::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<binder::unique_fd, binder::borrowed_fd>>* ancillaryFds) = 0;
/**
* Check whether any threads are blocked while polling the transport
@@ -177,10 +178,10 @@
void setPollingState(bool state) const { isPolling = state; }
public:
- base::unique_fd fd;
+ binder::unique_fd fd;
RpcTransportFd() = default;
- explicit RpcTransportFd(base::unique_fd &&descriptor)
+ explicit RpcTransportFd(binder::unique_fd&& descriptor)
: isPolling(false), fd(std::move(descriptor)) {}
RpcTransportFd(RpcTransportFd &&transportFd) noexcept
@@ -192,7 +193,7 @@
return *this;
}
- RpcTransportFd &operator=(base::unique_fd &&descriptor) noexcept {
+ RpcTransportFd& operator=(binder::unique_fd&& descriptor) noexcept {
fd = std::move(descriptor);
isPolling = false;
return *this;
diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h
index 9937842..95318b2 100644
--- a/libs/binder/include/binder/Trace.h
+++ b/libs/binder/include/binder/Trace.h
@@ -16,22 +16,36 @@
#pragma once
-#include <cutils/trace.h>
#include <stdint.h>
+#if __has_include(<cutils/trace.h>)
+#include <cutils/trace.h>
+#endif
+
+#ifdef ATRACE_TAG_AIDL
+#if ATRACE_TAG_AIDL != (1 << 24)
+#error "Mismatched ATRACE_TAG_AIDL definitions"
+#endif
+#else
+#define ATRACE_TAG_AIDL (1 << 24)
+#endif
+
namespace android {
namespace binder {
+// Forward declarations from internal OS.h
+namespace os {
// Trampoline functions allowing generated aidls to trace binder transactions without depending on
// libcutils/libutils
-void atrace_begin(uint64_t tag, const char* name);
-void atrace_end(uint64_t tag);
+void trace_begin(uint64_t tag, const char* name);
+void trace_end(uint64_t tag);
+} // namespace os
class ScopedTrace {
public:
- inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); }
+ inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { os::trace_begin(mTag, name); }
- inline ~ScopedTrace() { atrace_end(mTag); }
+ inline ~ScopedTrace() { os::trace_end(mTag); }
private:
uint64_t mTag;
diff --git a/libs/binder/include/binder/unique_fd.h b/libs/binder/include/binder/unique_fd.h
new file mode 100644
index 0000000..439b8a2
--- /dev/null
+++ b/libs/binder/include/binder/unique_fd.h
@@ -0,0 +1,114 @@
+/*
+ * 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
+
+#ifndef BINDER_NO_LIBBASE
+
+#include <android-base/unique_fd.h>
+
+namespace android::binder {
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+} // namespace android::binder
+
+#else // BINDER_NO_LIBBASE
+
+#include <errno.h>
+#include <fcntl.h> // not needed for unique_fd, but a lot of users depend on open(3)
+#include <unistd.h>
+
+namespace android::binder {
+
+// Container for a file descriptor that automatically closes the descriptor as
+// it goes out of scope.
+//
+// unique_fd ufd(open("/some/path", "r"));
+// if (!ufd.ok()) return error;
+//
+// // Do something useful with ufd.get(), possibly including early 'return'.
+//
+// return 0; // Descriptor is closed for you.
+//
+class unique_fd final {
+public:
+ unique_fd() {}
+
+ explicit unique_fd(int fd) { reset(fd); }
+ ~unique_fd() { reset(); }
+
+ unique_fd(const unique_fd&) = delete;
+ void operator=(const unique_fd&) = delete;
+ unique_fd(unique_fd&& other) noexcept { reset(other.release()); }
+ unique_fd& operator=(unique_fd&& s) noexcept {
+ int fd = s.fd_;
+ s.fd_ = -1;
+ reset(fd);
+ return *this;
+ }
+
+ [[clang::reinitializes]] void reset(int new_value = -1) {
+ int previous_errno = errno;
+
+ if (fd_ != -1) {
+ ::close(fd_);
+ }
+
+ fd_ = new_value;
+ errno = previous_errno;
+ }
+
+ int get() const { return fd_; }
+
+ bool ok() const { return get() >= 0; }
+
+ [[nodiscard]] int release() {
+ int ret = fd_;
+ fd_ = -1;
+ return ret;
+ }
+
+private:
+ int fd_ = -1;
+};
+
+// A wrapper type that can be implicitly constructed from either int or
+// unique_fd. This supports cases where you don't actually own the file
+// descriptor, and can't take ownership, but are temporarily acting as if
+// you're the owner.
+//
+// One example would be a function that needs to also allow
+// STDERR_FILENO, not just a newly-opened fd. Another example would be JNI code
+// that's using a file descriptor that's actually owned by a
+// ParcelFileDescriptor or whatever on the Java side, but where the JNI code
+// would like to enforce this weaker sense of "temporary ownership".
+//
+// If you think of unique_fd as being like std::string in that represents
+// ownership, borrowed_fd is like std::string_view (and int is like const
+// char*).
+struct borrowed_fd {
+ /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
+ /* implicit */ borrowed_fd(const unique_fd& ufd) : fd_(ufd.get()) {} // NOLINT
+
+ int get() const { return fd_; }
+
+private:
+ int fd_ = -1;
+};
+
+} // namespace android::binder
+
+#endif // BINDER_NO_LIBBASE
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f51cd9b..cb44c58 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -16,13 +16,18 @@
#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>
#include <binder/RpcSession.h>
+#include <binder/unique_fd.h>
+
+#ifndef __TRUSTY__
#include <cutils/sockets.h>
+#endif
+
+#ifdef __linux__
#include <linux/vm_sockets.h>
+#endif // __linux__
using android::OK;
using android::RpcServer;
@@ -30,7 +35,7 @@
using android::sp;
using android::status_t;
using android::statusToString;
-using android::base::unique_fd;
+using android::binder::unique_fd;
// Opaque handle for RpcServer.
struct ARpcServer {};
@@ -75,6 +80,7 @@
extern "C" {
+#ifndef __TRUSTY__
ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
auto server = RpcServer::make();
@@ -85,8 +91,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 +101,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 +115,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 +131,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,13 +147,14 @@
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));
return createObjectHandle<ARpcServer>(server);
}
+#endif // __TRUSTY__
void ARpcServer_setSupportedFileDescriptorTransportModes(
ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[],
@@ -188,11 +195,12 @@
freeObjectHandle<RpcSession>(handle);
}
+#ifndef __TRUSTY__
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 +211,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 +222,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,19 +237,20 @@
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());
}
+#endif // __TRUSTY__
AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
void* param) {
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 preconnected client. error: %s", statusToString(status).c_str());
return nullptr;
}
return AIBinder_fromPlatformBinder(session->getRootObject());
diff --git a/libs/binder/liblog_stub/Android.bp b/libs/binder/liblog_stub/Android.bp
new file mode 100644
index 0000000..f2ca22f
--- /dev/null
+++ b/libs/binder/liblog_stub/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_headers {
+ name: "liblog_stub",
+ export_include_dirs: ["include"],
+
+ host_supported: true,
+ native_bridge_supported: true,
+ product_available: true,
+ recovery_available: true,
+ vendor_available: true,
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ visibility: [
+ "//frameworks/native/libs/binder:__subpackages__",
+ "//system/core/libutils/binder",
+ ],
+}
diff --git a/libs/binder/liblog_stub/include/android/log.h b/libs/binder/liblog_stub/include/android/log.h
new file mode 100644
index 0000000..9dcd926
--- /dev/null
+++ b/libs/binder/liblog_stub/include/android/log.h
@@ -0,0 +1,49 @@
+/*
+ * 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
+
+extern "C" {
+
+/**
+ * Android log priority values, in increasing order of priority.
+ */
+typedef enum android_LogPriority {
+ /** For internal use only. */
+ ANDROID_LOG_UNKNOWN = 0,
+ /** The default priority, for internal use only. */
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+ /** Verbose logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_VERBOSE,
+ /** Debug logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_DEBUG,
+ /** Informational logging. Should typically be disabled for a release apk. */
+ ANDROID_LOG_INFO,
+ /** Warning logging. For use with recoverable failures. */
+ ANDROID_LOG_WARN,
+ /** Error logging. For use with unrecoverable failures. */
+ ANDROID_LOG_ERROR,
+ /** Fatal logging. For use when aborting. */
+ ANDROID_LOG_FATAL,
+ /** For internal use only. */
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+} android_LogPriority;
+
+typedef void (*__android_logger_function)(const struct __android_log_message* log_message);
+inline void __android_log_set_logger(__android_logger_function) {}
+inline void __android_log_stderr_logger(const struct __android_log_message*) {}
+
+} // extern "C"
diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h
new file mode 100644
index 0000000..91c9632
--- /dev/null
+++ b/libs/binder/liblog_stub/include/log/log.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+
+#include <android/log.h>
+
+extern "C" {
+
+#ifndef ANDROID_LOG_STUB_MIN_PRIORITY
+#define ANDROID_LOG_STUB_MIN_PRIORITY ANDROID_LOG_INFO
+#endif
+
+#ifndef LOG_TAG
+#define LOG_TAG ""
+#endif
+
+constexpr bool __android_log_stub_is_loggable(android_LogPriority priority) {
+ return ANDROID_LOG_STUB_MIN_PRIORITY <= priority;
+}
+
+#ifdef ANDROID_LOG_STUB_WEAK_PRINT
+#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT __android_log_print
+#define __ANDROID_LOG_STUB_PRINT_ATTR __attribute__((weak))
+#else
+#define __ANDROID_LOG_STUB_IS_PRINT_PRESENT true
+#define __ANDROID_LOG_STUB_PRINT_ATTR
+#endif
+
+int __android_log_print(int prio, const char* tag, const char* fmt, ...)
+ __attribute__((format(printf, 3, 4))) __ANDROID_LOG_STUB_PRINT_ATTR;
+
+#define IF_ALOG(priority, tag) \
+ if (__android_log_stub_is_loggable(ANDROID_##priority) && __ANDROID_LOG_STUB_IS_PRINT_PRESENT)
+#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG)
+#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG)
+#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG)
+#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
+#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
+
+#define ALOG(priority, tag, fmt, ...) \
+ do { \
+ if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \
+ std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \
+ } \
+ IF_ALOG(priority, tag) { \
+ __android_log_print(ANDROID_##priority, tag, \
+ tag ": " fmt "\n" __VA_OPT__(, ) __VA_ARGS__); \
+ } \
+ if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \
+ } while (false)
+#define ALOGV(...) ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define LOG_FATAL(...) ALOG(LOG_FATAL, LOG_TAG, __VA_ARGS__)
+#define LOG_ALWAYS_FATAL LOG_FATAL
+
+#define ALOG_IF(cond, priority, tag, ...) \
+ if (cond) [[unlikely]] \
+ ALOG(priority, tag, #cond ": " __VA_ARGS__)
+#define ALOGV_IF(cond, ...) ALOG_IF(cond, LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define ALOGD_IF(cond, ...) ALOG_IF(cond, LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGI_IF(cond, ...) ALOG_IF(cond, LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW_IF(cond, ...) ALOG_IF(cond, LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE_IF(cond, ...) ALOG_IF(cond, LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define LOG_FATAL_IF(cond, ...) ALOG_IF(cond, LOG_FATAL, LOG_TAG, __VA_ARGS__)
+#define LOG_ALWAYS_FATAL_IF LOG_FATAL_IF
+#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
+
+inline int android_errorWriteLog(int tag, const char* subTag) {
+ ALOGE("android_errorWriteLog(%x, %s)", tag, subTag);
+ return 0;
+}
+
+} // extern "C"
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 58ed418..ccf3ce8 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",
@@ -138,6 +139,7 @@
"performance*",
"portability*",
],
+ afdo: true,
}
cc_library_headers {
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..a0e4f7b
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -0,0 +1,515 @@
+/*
+ * 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 {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ 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) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(),
+ vec);
+ }
+ return false;
+ }
+ bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(),
+ vec);
+ }
+ return false;
+ }
+ bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(),
+ vec);
+ }
+ return false;
+ }
+ bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle,
+ key.c_str(), vec);
+ }
+ return false;
+ }
+
+ // 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) {
+ if (__builtin_available(android __ANDROID_API_V__, *)) {
+ 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/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index db2d2c1..b1ab7b0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -390,6 +390,12 @@
* calling process dies and is replaced with another process with elevated permissions and the same
* PID.
*
+ * Warning: oneway transactions do not receive PID. Even if you expect
+ * a transaction to be synchronous, a misbehaving client could send it
+ * as a synchronous call and result in a 0 PID here. Additionally, if
+ * there is a race and the calling process dies, the PID may still be
+ * 0 for a synchronous call.
+ *
* Available since API level 29.
*
* \return calling pid or the current process's PID if this thread isn't processing a transaction.
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 4786c89..14edf2b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -31,6 +31,11 @@
#include <stdint.h>
#include <sys/cdefs.h>
+#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
+#include <log/log.h>
+#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message)
+#endif
+
__BEGIN_DECLS
#ifndef __BIONIC__
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/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index c1f62e5..089c775 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -21,17 +21,15 @@
__BEGIN_DECLS
/**
- * Private addition to binder_flag_t.
+ * Indicates that this transaction is coupled w/ vendor.img
*/
-enum {
- /**
- * Indicates that this transaction is coupled w/ vendor.img
- */
- FLAG_PRIVATE_VENDOR = 0x10000000,
-};
+constexpr binder_flags_t FLAG_PRIVATE_VENDOR = 0x10000000;
#if defined(__ANDROID_VENDOR__)
+/**
+ * Private addition to binder_flag_t.
+ */
enum {
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
};
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..88ce5f4 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -16,24 +16,23 @@
#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 <binder/unique_fd.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;
using ::android::sp;
using ::android::status_t;
-using ::android::base::unique_fd;
+using ::android::binder::unique_fd;
using ::android::os::ParcelFileDescriptor;
template <typename T>
@@ -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..ca3d5e6 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
+#if defined(__ANDROID_VENDOR__) && !defined(__TRUSTY__)
+#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/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
index 551c59f..cb6993e 100644
--- a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
+++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt
@@ -8,4 +8,17 @@
--allowlist-type=AIBinder_DeathRecipient
--allowlist-type=AParcel
--allowlist-type=binder_status_t
+--blocklist-function="vprintf"
+--blocklist-function="strtold"
+--blocklist-function="_vtlog"
+--blocklist-function="vscanf"
+--blocklist-function="vfprintf_worker"
+--blocklist-function="vsprintf"
+--blocklist-function="vsnprintf"
+--blocklist-function="vsnprintf_filtered"
+--blocklist-function="vfscanf"
+--blocklist-function="vsscanf"
+--blocklist-function="vdprintf"
+--blocklist-function="vasprintf"
+--blocklist-function="strtold_l"
--allowlist-function=.*
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 788abc4..535ce01 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -70,7 +70,7 @@
// TODO(b/184872979): remove once the RPC Binder API is stabilised.
rust_bindgen {
name: "libbinder_rpc_unstable_bindgen",
- wrapper_src: ":libbinder_rpc_unstable_header",
+ wrapper_src: "BinderBindings.hpp",
crate_name: "binder_rpc_unstable_bindgen",
visibility: [":__subpackages__"],
source_stem: "bindings",
diff --git a/libs/binder/rust/rpcbinder/BinderBindings.hpp b/libs/binder/rust/rpcbinder/BinderBindings.hpp
new file mode 100644
index 0000000..7feb965
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/BinderBindings.hpp
@@ -0,0 +1 @@
+#include <binder_rpc_unstable.hpp>
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index a957385..163f000 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -16,8 +16,10 @@
//! API for RPC Binder services.
+#[cfg(not(target_os = "trusty"))]
mod server;
mod session;
+#[cfg(not(target_os = "trusty"))]
pub use server::{RpcServer, RpcServerRef};
pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 79a9510..09688a2 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -17,11 +17,8 @@
use binder::unstable_api::new_spibinder;
use binder::{FromIBinder, SpIBinder, StatusCode, Strong};
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
-use std::ffi::CString;
-use std::os::{
- raw::{c_int, c_void},
- unix::io::{AsRawFd, BorrowedFd, RawFd},
-};
+use std::os::fd::RawFd;
+use std::os::raw::{c_int, c_void};
pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode;
@@ -87,6 +84,7 @@
}
/// Connects to an RPC Binder server over vsock for a particular interface.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_vsock_client<T: FromIBinder + ?Sized>(
&self,
cid: u32,
@@ -106,11 +104,12 @@
/// Connects to an RPC Binder server over a names Unix Domain Socket for
/// a particular interface.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>(
&self,
socket_name: &str,
) -> Result<Strong<T>, StatusCode> {
- let socket_name = match CString::new(socket_name) {
+ let socket_name = match std::ffi::CString::new(socket_name) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
@@ -131,10 +130,12 @@
/// Connects to an RPC Binder server over a bootstrap Unix Domain Socket
/// for a particular interface.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>(
&self,
- bootstrap_fd: BorrowedFd,
+ bootstrap_fd: std::os::fd::BorrowedFd,
) -> Result<Strong<T>, StatusCode> {
+ use std::os::fd::AsRawFd;
// SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take
// ownership of bootstrap_fd. The returned AIBinder has correct
// reference count, and the ownership can safely be taken by new_spibinder.
@@ -148,12 +149,13 @@
}
/// Connects to an RPC Binder server over inet socket at the given address and port.
+ #[cfg(not(target_os = "trusty"))]
pub fn setup_inet_client<T: FromIBinder + ?Sized>(
&self,
address: &str,
port: u32,
) -> Result<Strong<T>, StatusCode> {
- let address = match CString::new(address) {
+ let address = match std::ffi::CString::new(address) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
@@ -173,6 +175,22 @@
Self::get_interface(service)
}
+ #[cfg(target_os = "trusty")]
+ pub fn setup_trusty_client<T: FromIBinder + ?Sized>(
+ &self,
+ port: &std::ffi::CStr,
+ ) -> Result<Strong<T>, StatusCode> {
+ self.setup_preconnected_client(|| {
+ let h = tipc::Handle::connect(port)
+ .expect("Failed to connect to service port {SERVICE_PORT}");
+
+ // Do not close the handle at the end of the scope
+ let fd = h.as_raw_fd();
+ core::mem::forget(h);
+ Some(fd)
+ })
+ }
+
/// Connects to an RPC Binder server, using the given callback to get (and
/// take ownership of) file descriptors already connected to it.
pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index a08cb7a..e34d31e 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -21,16 +21,17 @@
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::fd::AsRawFd;
use std::os::raw::c_char;
-use std::os::unix::io::AsRawFd;
use std::ptr;
/// Binder action to perform.
@@ -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/lib.rs b/libs/binder/rust/src/lib.rs
index ed870b6..7f9348d 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -100,6 +100,7 @@
mod native;
mod parcel;
mod proxy;
+#[cfg(not(target_os = "trusty"))]
mod state;
use binder_ndk_sys as sys;
@@ -116,6 +117,7 @@
get_declared_instances, get_interface, get_service, is_declared, wait_for_interface,
wait_for_service, DeathRecipient, SpIBinder, WpIBinder,
};
+#[cfg(not(target_os = "trusty"))]
pub use state::{ProcessState, ThreadState};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index b248f5e..8ae010e 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -24,12 +24,10 @@
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;
-use std::os::unix::io::FromRawFd;
-use std::slice;
use std::sync::Mutex;
/// Rust wrapper around Binder remotable objects.
@@ -330,6 +328,7 @@
/// contains a `T` pointer in its user data. fd should be a non-owned file
/// descriptor, and args must be an array of null-terminated string
/// pointers with length num_args.
+ #[cfg(not(target_os = "trusty"))]
unsafe extern "C" fn on_dump(
binder: *mut sys::AIBinder,
fd: i32,
@@ -339,9 +338,10 @@
if fd < 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
+ use std::os::fd::FromRawFd;
// 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(std::fs::File::from_raw_fd(fd)) };
if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
@@ -353,7 +353,7 @@
// Safety: Our caller promised that `args` is an array of
// null-terminated string pointers with length `num_args`.
unsafe {
- slice::from_raw_parts(args, num_args as usize)
+ std::slice::from_raw_parts(args, num_args as usize)
.iter()
.map(|s| CStr::from_ptr(*s))
.collect()
@@ -366,13 +366,26 @@
// 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,
Err(e) => e as status_t,
}
}
+
+ /// Called to handle the `dump` transaction.
+ #[cfg(target_os = "trusty")]
+ unsafe extern "C" fn on_dump(
+ _binder: *mut sys::AIBinder,
+ _fd: i32,
+ _args: *mut *const c_char,
+ _num_args: u32,
+ ) -> status_t {
+ // This operation is not supported on Trusty right now
+ // because we do not have a uniform way of writing to handles
+ StatusCode::INVALID_OPERATION as status_t
+ }
}
impl<T: Remotable> Drop for Binder<T> {
@@ -569,7 +582,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/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/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index dad3379..7434e9d 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -32,8 +32,8 @@
use std::ffi::{c_void, CStr, CString};
use std::fmt;
use std::mem;
+use std::os::fd::AsRawFd;
use std::os::raw::c_char;
-use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::Arc;
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index a3a2562..8a06274 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -101,13 +101,16 @@
/// dies and is replaced with another process with elevated permissions and
/// the same PID.
///
+ /// Warning: oneway transactions do not receive PID. Even if you expect
+ /// a transaction to be synchronous, a misbehaving client could send it
+ /// as a synchronous call and result in a 0 PID here. Additionally, if
+ /// there is a race and the calling process dies, the PID may still be
+ /// 0 for a synchronous call.
+ ///
/// Available since API level 29.
///
/// \return calling pid or the current process's PID if this thread isn't
/// processing a transaction.
- ///
- /// If the transaction being processed is a oneway transaction, then this
- /// method will return 0.
pub fn get_calling_pid() -> pid_t {
// Safety: Safe FFI
unsafe { sys::AIBinder_getCallingPid() }
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/rust/tests/serialization.cpp b/libs/binder/rust/tests/serialization.cpp
index 3f59dab..0cdf8c5 100644
--- a/libs/binder/rust/tests/serialization.cpp
+++ b/libs/binder/rust/tests/serialization.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include "serialization.hpp"
+#include "../../FdUtils.h"
+#include "../../tests/FileUtils.h"
+
#include <android/binder_ibinder_platform.h>
#include <android/binder_libbinder.h>
#include <binder/IServiceManager.h>
@@ -24,8 +28,6 @@
#include <gtest/gtest.h>
#include <utils/Errors.h>
#include <utils/String16.h>
-#include "android-base/file.h"
-#include "serialization.hpp"
#include <cmath>
#include <cstdint>
@@ -34,7 +36,7 @@
using namespace std;
using namespace android;
-using android::base::unique_fd;
+using android::binder::unique_fd;
using android::os::ParcelFileDescriptor;
// defined in Rust
@@ -349,7 +351,7 @@
TEST_F(SerializationTest, SerializeFileDescriptor) {
unique_fd out_file, in_file;
- ASSERT_TRUE(base::Pipe(&out_file, &in_file));
+ ASSERT_TRUE(binder::Pipe(&out_file, &in_file));
vector<ParcelFileDescriptor> file_descriptors;
file_descriptors.push_back(ParcelFileDescriptor(std::move(out_file)));
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 5bf9680..18b178b 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -17,12 +17,11 @@
#include <sysexits.h>
#include <unistd.h>
+#include <filesystem>
#include <iostream>
-#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>
@@ -31,6 +30,8 @@
#include <binder/ProcessState.h>
#include <binder/RpcServer.h>
+#include "file.h"
+
using android::BBinder;
using android::defaultServiceManager;
using android::OK;
@@ -39,14 +40,12 @@
using android::status_t;
using android::statusToString;
using android::String16;
-using android::base::Basename;
using android::base::GetBoolProperty;
using android::base::InitLogging;
using android::base::LogdLogger;
using android::base::LogId;
using android::base::LogSeverity;
using android::base::StdioLogger;
-using android::base::StringPrintf;
using std::string_view_literals::operator""sv;
namespace {
@@ -55,13 +54,14 @@
using ServiceRetriever = decltype(&android::IServiceManager::checkService);
using android::debug::IAdbManager;
-int Usage(const char* program) {
- auto basename = Basename(program);
- auto format = R"(dispatch calls to RPC service.
+int Usage(std::filesystem::path program) {
+ auto basename = program.filename();
+ // 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 +71,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;
}
@@ -254,7 +254,8 @@
mLogdLogger(id, severity, tag, file, line, message);
if (severity >= LogSeverity::WARNING) {
std::cout << std::flush;
- std::cerr << Basename(getprogname()) << ": " << message << std::endl;
+ auto progname = std::filesystem::path(getprogname()).filename();
+ std::cerr << progname << ": " << message << std::endl;
}
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index cd3e7c0..dd2be94 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -134,6 +134,10 @@
"IBinderRpcTest.aidl",
"ParcelableCertificateData.aidl",
],
+ flags: [
+ "-Werror",
+ "-Wno-mixed-oneway",
+ ],
backend: {
java: {
enabled: false,
@@ -172,6 +176,30 @@
],
}
+cc_library_static {
+ name: "libbinder_test_utils",
+ host_supported: true,
+ vendor_available: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ defaults: [
+ "binder_test_defaults",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ srcs: [
+ "FileUtils.cpp",
+ ],
+ visibility: [
+ ":__subpackages__",
+ ],
+}
+
cc_defaults {
name: "binderRpcTest_common_defaults",
host_supported: true,
@@ -185,6 +213,7 @@
],
static_libs: [
+ "libbinder_test_utils",
"libbinder_tls_static",
"libbinder_tls_test_utils",
"binderRpcTestIface-cpp",
@@ -227,6 +256,12 @@
// contention on the device. b/276820894
test_options: {
unit_test: false,
+ test_runner_options: [
+ {
+ name: "native-test-timeout",
+ value: "10m",
+ },
+ ],
},
test_suites: ["general-tests"],
@@ -801,6 +836,7 @@
],
// Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
hotlists: ["4637097"],
+ use_for_presubmit: true,
},
}
diff --git a/libs/binder/tests/FileUtils.cpp b/libs/binder/tests/FileUtils.cpp
new file mode 100644
index 0000000..61509fe
--- /dev/null
+++ b/libs/binder/tests/FileUtils.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 "FileUtils.h"
+
+#ifdef BINDER_NO_LIBBASE
+
+#include <sys/stat.h>
+#include <filesystem>
+
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#endif
+#if defined(_WIN32)
+#include <direct.h>
+#include <windows.h>
+#endif
+
+namespace android::binder {
+
+bool ReadFdToString(borrowed_fd fd, std::string* content) {
+ content->clear();
+
+ // Although original we had small files in mind, this code gets used for
+ // very large files too, where the std::string growth heuristics might not
+ // be suitable. https://code.google.com/p/android/issues/detail?id=258500.
+ struct stat sb;
+ if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
+ content->reserve(sb.st_size);
+ }
+
+ char buf[4096] __attribute__((__uninitialized__));
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
+ content->append(buf, n);
+ }
+ return (n == 0) ? true : false;
+}
+
+bool WriteStringToFd(std::string_view content, borrowed_fd fd) {
+ const char* p = content.data();
+ size_t left = content.size();
+ while (left > 0) {
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
+ if (n == -1) {
+ return false;
+ }
+ p += n;
+ left -= n;
+ }
+ return true;
+}
+
+static std::filesystem::path GetExecutablePath2() {
+#if defined(__linux__)
+ return std::filesystem::read_symlink("/proc/self/exe");
+#elif defined(__APPLE__)
+ char path[PATH_MAX + 1];
+ uint32_t path_len = sizeof(path);
+ int rc = _NSGetExecutablePath(path, &path_len);
+ if (rc < 0) {
+ std::unique_ptr<char> path_buf(new char[path_len]);
+ _NSGetExecutablePath(path_buf.get(), &path_len);
+ return path_buf.get();
+ }
+ return path;
+#elif defined(_WIN32)
+ char path[PATH_MAX + 1];
+ DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
+ if (result == 0 || result == sizeof(path) - 1) return "";
+ path[PATH_MAX - 1] = 0;
+ return path;
+#elif defined(__EMSCRIPTEN__)
+ abort();
+#else
+#error unknown OS
+#endif
+}
+
+std::string GetExecutableDirectory() {
+ return GetExecutablePath2().parent_path();
+}
+
+} // namespace android::binder
+
+#endif // BINDER_NO_LIBBASE
diff --git a/libs/binder/tests/FileUtils.h b/libs/binder/tests/FileUtils.h
new file mode 100644
index 0000000..2cbe5e7
--- /dev/null
+++ b/libs/binder/tests/FileUtils.h
@@ -0,0 +1,49 @@
+/*
+ * 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 "../file.h"
+
+#ifndef BINDER_NO_LIBBASE
+
+namespace android::binder {
+using android::base::GetExecutableDirectory;
+using android::base::ReadFdToString;
+using android::base::WriteStringToFd;
+} // namespace android::binder
+
+#else // BINDER_NO_LIBBASE
+
+#include <binder/unique_fd.h>
+
+#include <string_view>
+
+#if !defined(_WIN32) && !defined(O_BINARY)
+/** Windows needs O_BINARY, but Unix never mangles line endings. */
+#define O_BINARY 0
+#endif
+
+namespace android::binder {
+
+bool ReadFdToString(borrowed_fd fd, std::string* content);
+bool WriteStringToFd(std::string_view content, borrowed_fd fd);
+
+std::string GetExecutableDirectory();
+
+} // namespace android::binder
+
+#endif // BINDER_NO_LIBBASE
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..cb1a1ee 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 <binder/unique_fd.h>
+#include <utils/Flattenable.h>
#include <linux/sched.h>
#include <sys/epoll.h>
@@ -52,10 +52,12 @@
#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;
using android::base::testing::Ok;
+using android::binder::unique_fd;
using testing::ExplainMatchResult;
using testing::Matcher;
using testing::Not;
@@ -846,7 +848,7 @@
writebuf[i] = i;
}
- android::base::unique_fd read_end, write_end;
+ unique_fd read_end, write_end;
{
int pipefd[2];
ASSERT_EQ(0, pipe2(pipefd, O_NONBLOCK));
@@ -1176,7 +1178,7 @@
Parcel reply;
EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_NON_BLOCKING_FD, {} /*data*/, &reply),
StatusEq(NO_ERROR));
- base::unique_fd fd;
+ unique_fd fd;
EXPECT_THAT(reply.readUniqueFileDescriptor(&fd), StatusEq(OK));
const int result = fcntl(fd.get(), F_GETFL);
@@ -1325,7 +1327,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);
@@ -1485,7 +1487,7 @@
BinderLibTest::SetUp();
}
- std::tuple<android::base::unique_fd, unsigned int> CreateSocket() {
+ std::tuple<unique_fd, unsigned int> CreateSocket() {
auto rpcServer = RpcServer::make();
EXPECT_NE(nullptr, rpcServer);
if (rpcServer == nullptr) return {};
@@ -1552,7 +1554,7 @@
TEST_P(BinderLibRpcTestP, SetRpcClientDebugNoFd) {
auto binder = GetService();
ASSERT_TRUE(binder != nullptr);
- EXPECT_THAT(binder->setRpcClientDebug(android::base::unique_fd(), sp<BBinder>::make()),
+ EXPECT_THAT(binder->setRpcClientDebug(unique_fd(), sp<BBinder>::make()),
Debuggable(StatusEq(BAD_VALUE)));
}
@@ -1822,7 +1824,7 @@
int ret;
int32_t size;
const void *buf;
- android::base::unique_fd fd;
+ unique_fd fd;
ret = data.readUniqueParcelFileDescriptor(&fd);
if (ret != NO_ERROR) {
@@ -1887,7 +1889,7 @@
ALOGE("Could not make socket non-blocking: %s", strerror(errno));
return UNKNOWN_ERROR;
}
- base::unique_fd out(sockets[0]);
+ unique_fd out(sockets[0]);
status_t writeResult = reply->writeUniqueFileDescriptor(out);
if (writeResult != NO_ERROR) {
ALOGE("Could not write unique_fd");
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 0a0dae0..34fc43f 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -29,8 +29,8 @@
using android::status_t;
using android::String16;
using android::String8;
-using android::base::unique_fd;
using android::binder::Status;
+using android::binder::unique_fd;
TEST(Parcel, NonNullTerminatedString8) {
String8 kTestString = String8("test-is-good");
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index d08a9bb..73c0a94 100644
--- a/libs/binder/tests/binderRecordReplayTest.cpp
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -15,15 +15,14 @@
*/
#include <BnBinderRecordReplayTest.h>
-#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/RecordedTransaction.h>
+#include <binder/unique_fd.h>
#include <fuzzbinder/libbinder_driver.h>
#include <fuzzer/FuzzedDataProvider.h>
@@ -33,11 +32,14 @@
#include <sys/prctl.h>
+#include "../file.h"
#include "parcelables/SingleDataParcelable.h"
using namespace android;
using android::generateSeedsFromRecording;
+using android::binder::borrowed_fd;
using android::binder::Status;
+using android::binder::unique_fd;
using android::binder::debug::RecordedTransaction;
using parcelables::SingleDataParcelable;
@@ -91,7 +93,7 @@
GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
};
-std::vector<uint8_t> retrieveData(base::borrowed_fd fd) {
+std::vector<uint8_t> retrieveData(borrowed_fd fd) {
struct stat fdStat;
EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1);
EXPECT_TRUE(fdStat.st_size != 0);
@@ -103,8 +105,8 @@
}
void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& transaction) {
- base::unique_fd seedFd(open("/data/local/tmp/replayFuzzService",
- O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, 0666));
+ unique_fd seedFd(open("/data/local/tmp/replayFuzzService",
+ O_RDWR | O_CREAT | O_CLOEXEC | O_TRUNC, 0666));
ASSERT_TRUE(seedFd.ok());
// generate corpus from this transaction.
@@ -148,8 +150,8 @@
Status (IBinderRecordReplayTest::*get)(U*), U changedValue) {
auto replayFunctions = {&replayBinder, &replayFuzzService};
for (auto replayFunc : replayFunctions) {
- base::unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
- O_RDWR | O_CREAT | O_CLOEXEC, 0666));
+ unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec",
+ O_RDWR | O_CREAT | O_CLOEXEC, 0666));
ASSERT_TRUE(fd.ok());
// record a transaction
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
index 30172cc..6eb78d0 100644
--- a/libs/binder/tests/binderRecordedTransactionTest.cpp
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -20,7 +20,7 @@
using android::Parcel;
using android::status_t;
-using android::base::unique_fd;
+using android::binder::unique_fd;
using android::binder::debug::RecordedTransaction;
TEST(BinderRecordedTransaction, RoundTripEncoding) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 1340ea1..2769a88 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>
@@ -26,6 +25,7 @@
#include <thread>
#include <type_traits>
+#include <dirent.h>
#include <dlfcn.h>
#include <poll.h>
#include <sys/prctl.h>
@@ -36,11 +36,16 @@
#include <trusty/tipc.h>
#endif // BINDER_RPC_TO_TRUSTY_TEST
+#include "../Utils.h"
#include "binderRpcTestCommon.h"
#include "binderRpcTestFixture.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
+using android::binder::borrowed_fd;
+using android::binder::GetExecutableDirectory;
+using android::binder::ReadFdToString;
+using android::binder::unique_fd;
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
@@ -59,12 +64,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) {
@@ -83,12 +88,11 @@
mPid = other.mPid;
other.mPid = 0;
}
- Process(const std::function<void(android::base::borrowed_fd /* writeEnd */,
- 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);
+ Process(const std::function<void(borrowed_fd /* writeEnd */, borrowed_fd /* readEnd */)>& f) {
+ unique_fd childWriteEnd;
+ unique_fd childReadEnd;
+ if (!binder::Pipe(&mReadEnd, &childWriteEnd, 0)) PLOGF("child write pipe failed");
+ if (!binder::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);
@@ -110,8 +114,8 @@
}
}
}
- android::base::borrowed_fd readEnd() { return mReadEnd; }
- android::base::borrowed_fd writeEnd() { return mWriteEnd; }
+ borrowed_fd readEnd() { return mReadEnd; }
+ borrowed_fd writeEnd() { return mWriteEnd; }
void setCustomExitStatusCheck(std::function<void(int wstatus)> f) {
mCustomExitStatusCheck = std::move(f);
@@ -125,8 +129,8 @@
private:
std::function<void(int wstatus)> mCustomExitStatusCheck;
pid_t mPid = 0;
- android::base::unique_fd mReadEnd;
- android::base::unique_fd mWriteEnd;
+ unique_fd mReadEnd;
+ unique_fd mWriteEnd;
};
static std::string allocateSocketAddress() {
@@ -142,12 +146,13 @@
return vsockPort++;
}
-static base::unique_fd initUnixSocket(std::string addr) {
+static unique_fd initUnixSocket(std::string addr) {
auto socket_addr = UnixSocketAddress(addr.c_str());
- base::unique_fd fd(
- TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX)));
- CHECK(fd.ok());
- CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize())));
+ unique_fd fd(TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX)));
+ 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;
}
@@ -202,37 +207,33 @@
void terminate() override { host.terminate(); }
};
-static base::unique_fd connectTo(const RpcSocketAddress& addr) {
- base::unique_fd serverFd(
+static unique_fd connectTo(const RpcSocketAddress& addr) {
+ 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;
}
#ifndef BINDER_RPC_TO_TRUSTY_TEST
-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);
+static unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
+ unique_fd sockClient, sockServer;
+ if (!binder::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
+ PLOGF("Failed socketpair()");
}
int zero = 0;
iovec iov{&zero, sizeof(zero)};
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ std::vector<std::variant<unique_fd, borrowed_fd>> fds;
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 +247,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;
@@ -259,12 +262,12 @@
bool singleThreaded = GetParam().singleThreaded;
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" : "");
+ std::string path = GetExecutableDirectory();
+ auto servicePath =
+ std::format("{}/binder_rpc_test_service{}{}", path,
+ singleThreaded ? "_single_threaded" : "", noKernel ? "_no_kernel" : "");
- base::unique_fd bootstrapClientFd, socketFd;
+ unique_fd bootstrapClientFd, socketFd;
auto addr = allocateSocketAddress();
// Initializes the socket before the fork/exec.
@@ -273,14 +276,13 @@
} else if (socketType == SocketType::UNIX_BOOTSTRAP) {
// 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);
+ if (!binder::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) {
+ PLOGF("Failed socketpair()");
}
}
auto ret = std::make_unique<LinuxProcessSession>(
- Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) {
+ Process([=](borrowed_fd writeEnd, borrowed_fd readEnd) {
if (socketType == SocketType::TIPC) {
// Trusty has a single persistent service
return;
@@ -288,8 +290,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 +338,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 +360,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);
@@ -373,7 +377,7 @@
break;
case SocketType::UNIX_BOOTSTRAP:
status = session->setupUnixDomainSocketBootstrapClient(
- base::unique_fd(dup(bootstrapClientFd.get())));
+ unique_fd(dup(bootstrapClientFd.get())));
break;
case SocketType::VSOCK:
status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
@@ -390,14 +394,14 @@
// in case the service is slow to start
int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
if (tipcFd >= 0) {
- return android::base::unique_fd(tipcFd);
+ return unique_fd(tipcFd);
}
usleep(50000);
}
- return android::base::unique_fd();
+ return unique_fd();
#else
LOG_ALWAYS_FATAL("Tried to connect to Trusty outside of vendor");
- return android::base::unique_fd();
+ return unique_fd();
#endif
});
break;
@@ -408,7 +412,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 +593,12 @@
android::os::ParcelFileDescriptor fdA;
EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA));
std::string result;
- CHECK(android::base::ReadFdToString(fdA.get(), &result));
+ ASSERT_TRUE(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(ReadFdToString(fdB.get(), &result));
EXPECT_EQ(result, "b");
saturateThreadPool(kNumServerThreads, proc.rootIface);
@@ -951,8 +955,8 @@
ASSERT_TRUE(status.isOk()) << status;
std::string result;
- CHECK(android::base::ReadFdToString(out.get(), &result));
- EXPECT_EQ(result, "hello");
+ ASSERT_TRUE(ReadFdToString(out.get(), &result));
+ ASSERT_EQ(result, "hello");
}
TEST_P(BinderRpc, SendFiles) {
@@ -981,7 +985,7 @@
ASSERT_TRUE(status.isOk()) << status;
std::string result;
- CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_TRUE(ReadFdToString(out.get(), &result));
EXPECT_EQ(result, "123abcd");
}
@@ -1006,7 +1010,7 @@
ASSERT_TRUE(status.isOk()) << status;
std::string result;
- CHECK(android::base::ReadFdToString(out.get(), &result));
+ EXPECT_TRUE(ReadFdToString(out.get(), &result));
EXPECT_EQ(result, std::string(253, 'a'));
}
@@ -1151,14 +1155,14 @@
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
- android::base::unique_fd serverFd(
+ unique_fd serverFd(
TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
if (errno == EAFNOSUPPORT) {
return false;
}
- LOG_ALWAYS_FATAL_IF(serverFd == -1, "Could not create socket: %s", strerror(errno));
+ LOG_ALWAYS_FATAL_IF(!serverFd.ok(), "Could not create socket: %s", strerror(errno));
sockaddr_vm serverAddr{
.svm_family = AF_VSOCK,
@@ -1178,9 +1182,9 @@
// to see if the kernel supports it. It's safe to use a blocking
// connect because vsock sockets have a 2 second connection timeout,
// and they return ETIMEDOUT after that.
- android::base::unique_fd connectFd(
+ unique_fd connectFd(
TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
- LOG_ALWAYS_FATAL_IF(connectFd == -1, "Could not create socket for port %u: %s", vsockPort,
+ LOG_ALWAYS_FATAL_IF(!connectFd.ok(), "Could not create socket for port %u: %s", vsockPort,
strerror(errno));
bool success = false;
@@ -1192,13 +1196,13 @@
ret = TEMP_FAILURE_RETRY(connect(connectFd.get(), reinterpret_cast<sockaddr*>(&connectAddr),
sizeof(connectAddr)));
if (ret != 0 && (errno == EAGAIN || errno == EINPROGRESS)) {
- android::base::unique_fd acceptFd;
+ unique_fd acceptFd;
while (true) {
pollfd pfd[]{
{.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 +1363,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 +1395,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 "
@@ -1419,14 +1426,14 @@
};
TEST_P(BinderRpcServerOnly, SetExternalServerTest) {
- base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
+ unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
int sinkFd = sink.get();
auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
ASSERT_FALSE(server->hasServer());
ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
ASSERT_TRUE(server->hasServer());
- base::unique_fd retrieved = server->releaseServer();
+ unique_fd retrieved = server->releaseServer();
ASSERT_FALSE(server->hasServer());
ASSERT_EQ(sinkFd, retrieved.get());
}
@@ -1472,12 +1479,12 @@
// in the client half of the tests.
using Param =
std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>;
- using ConnectToServer = std::function<base::unique_fd()>;
+ using ConnectToServer = std::function<unique_fd()>;
// A server that handles client socket connections.
class Server {
public:
- using AcceptConnection = std::function<base::unique_fd(Server*)>;
+ using AcceptConnection = std::function<unique_fd(Server*)>;
explicit Server() {}
Server(Server&&) = default;
@@ -1506,8 +1513,8 @@
};
} break;
case SocketType::UNIX_BOOTSTRAP: {
- base::unique_fd bootstrapFdClient, bootstrapFdServer;
- if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) {
+ unique_fd bootstrapFdClient, bootstrapFdServer;
+ if (!binder::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) {
return AssertionFailure() << "Socketpair() failed";
}
auto status = rpcServer->setupUnixDomainSocketBootstrapServer(
@@ -1550,7 +1557,7 @@
mConnectToServer = [port] {
const char* addr = kLocalInetAddress;
auto aiStart = InetSocketAddress::getAddrInfo(addr, port);
- if (aiStart == nullptr) return base::unique_fd{};
+ if (aiStart == nullptr) return unique_fd{};
for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) {
auto fd = connectTo(
InetSocketAddress(ai->ai_addr, ai->ai_addrlen, addr, port));
@@ -1558,7 +1565,7 @@
}
ALOGE("None of the socket address resolved for %s:%u can be connected",
addr, port);
- return base::unique_fd{};
+ return unique_fd{};
};
} break;
case SocketType::TIPC: {
@@ -1582,24 +1589,22 @@
mThread = std::make_unique<std::thread>(&Server::run, this);
}
- base::unique_fd acceptServerConnection() {
- return base::unique_fd(TEMP_FAILURE_RETRY(
+ unique_fd acceptServerConnection() {
+ return unique_fd(TEMP_FAILURE_RETRY(
accept4(mFd.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK)));
}
- base::unique_fd recvmsgServerConnection() {
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ unique_fd recvmsgServerConnection() {
+ std::vector<std::variant<unique_fd, borrowed_fd>> fds;
int buf;
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();
- }
- return std::move(std::get<base::unique_fd>(fds[0]));
+ LOG_ALWAYS_FATAL_IF(fds.size() != 1, "Expected one FD from receiveMessage(), got %zu",
+ fds.size());
+ return std::move(std::get<unique_fd>(fds[0]));
}
void run() {
@@ -1607,13 +1612,13 @@
std::vector<std::thread> threads;
while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
- base::unique_fd acceptedFd = mAcceptConnection(this);
+ unique_fd acceptedFd = mAcceptConnection(this);
threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
}
for (auto& thread : threads) thread.join();
}
- void handleOne(android::base::unique_fd acceptedFd) {
+ void handleOne(unique_fd acceptedFd) {
ASSERT_TRUE(acceptedFd.ok());
RpcTransportFd transportFd(std::move(acceptedFd));
auto serverTransport = mCtx->newTransport(std::move(transportFd), mFdTrigger.get());
@@ -2083,7 +2088,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..62fe9e5 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,11 @@
#include <string>
#include <vector>
-#ifndef __TRUSTY__
-#include <android-base/file.h>
-#include <android-base/logging.h>
+#ifdef __ANDROID__
#include <android-base/properties.h>
+#endif
+
+#ifndef __TRUSTY__
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
#include <binder/ProcessState.h>
@@ -57,7 +57,10 @@
#include "../BuildFlags.h"
#include "../FdTrigger.h"
+#include "../FdUtils.h"
#include "../RpcState.h" // for debugging
+#include "FileUtils.h"
+#include "format.h"
#include "utils/Errors.h"
namespace android {
@@ -74,8 +77,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 +93,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 {
@@ -155,33 +157,34 @@
};
#ifndef __TRUSTY__
-static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
+static inline void writeString(binder::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::binder::WriteFully(fd, &length, sizeof(length)));
+ LOG_ALWAYS_FATAL_IF(!android::binder::WriteFully(fd, str.data(), str.length()));
}
-static inline std::string readString(android::base::borrowed_fd fd) {
+static inline std::string readString(binder::borrowed_fd fd) {
uint64_t length;
- CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+ LOG_ALWAYS_FATAL_IF(!android::binder::ReadFully(fd, &length, sizeof(length)));
std::string ret(length, '\0');
- CHECK(android::base::ReadFully(fd, ret.data(), length));
+ LOG_ALWAYS_FATAL_IF(!android::binder::ReadFully(fd, ret.data(), length));
return ret;
}
-static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
+static inline void writeToFd(binder::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()));
}
template <typename T>
-static inline T readFromFd(android::base::borrowed_fd fd) {
+static inline T readFromFd(binder::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;
}
@@ -206,12 +209,12 @@
}
// 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);
+static inline binder::unique_fd mockFileDescriptor(std::string contents) {
+ binder::unique_fd readFd, writeFd;
+ LOG_ALWAYS_FATAL_IF(!binder::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)) {
+ if (!android::binder::WriteStringToFd(contents, writeFd)) {
int savedErrno = errno;
LOG_ALWAYS_FATAL_IF(EPIPE != savedErrno, "mockFileDescriptor write failed: %s",
strerror(savedErrno));
@@ -390,7 +393,7 @@
}
if (delayed) {
- RpcMaybeThread([=]() {
+ RpcMaybeThread([=, this]() {
ALOGE("Executing delayed callback: '%s'", value.c_str());
Status status = doCallback(callback, oneway, false, value);
ALOGE("Delayed callback status: '%s'", status.toString8().c_str());
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 7435f30..28125f1 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -17,6 +17,8 @@
#include "binderRpcTestCommon.h"
using namespace android;
+using android::binder::ReadFdToString;
+using android::binder::unique_fd;
class MyBinderRpcTestAndroid : public MyBinderRpcTestBase {
public:
@@ -65,17 +67,17 @@
std::string acc;
for (const auto& file : files) {
std::string result;
- CHECK(android::base::ReadFdToString(file.get(), &result));
+ LOG_ALWAYS_FATAL_IF(!ReadFdToString(file.get(), &result));
acc.append(result);
}
out->reset(mockFileDescriptor(acc));
return Status::ok();
}
- HandoffChannel<android::base::unique_fd> mFdChannel;
+ HandoffChannel<unique_fd> mFdChannel;
Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override {
- mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0)));
+ mFdChannel.write(unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0)));
return Status::ok();
}
@@ -98,11 +100,11 @@
};
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]));
- base::unique_fd readEnd(atoi(argv[2]));
+ unique_fd writeEnd(atoi(argv[1]));
+ unique_fd readEnd(atoi(argv[2]));
auto serverConfig = readFromFd<BinderRpcTestServerConfig>(readEnd);
auto socketType = static_cast<SocketType>(serverConfig.socketType);
@@ -118,33 +120,36 @@
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);
unsigned int outPort = 0;
- base::unique_fd socketFd(serverConfig.socketFd);
+ unique_fd socketFd(serverConfig.socketFd);
switch (socketType) {
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 +164,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 +186,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..8346b36 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;
@@ -79,16 +77,14 @@
// Message size needs to be large enough to cover all messages sent by the
// tests: SendAndGetResultBackBig sends two large strings.
constexpr size_t max_msg_size = 4096;
- auto serverOrErr =
+ auto server =
RpcServerTrusty::make(hset, serverInfo.port->c_str(),
std::shared_ptr<const RpcServerTrusty::PortAcl>(&port_acl),
max_msg_size);
- if (!serverOrErr.ok()) {
- TLOGE("Failed to create RpcServer (%d)\n", serverOrErr.error());
+ if (server == nullptr) {
return EXIT_FAILURE;
}
- auto server = std::move(*serverOrErr);
serverInfo.server = server;
if (!serverInfo.server->setProtocolVersion(serverVersion)) {
return EXIT_FAILURE;
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index fcb83bd..18751cc 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -16,13 +16,14 @@
#define LOG_TAG "binderRpcTest"
-#include <android-base/stringprintf.h>
#include <binder/RpcTransportTipcTrusty.h>
#include <trusty-gtest.h>
#include <trusty_ipc.h>
#include "binderRpcTestFixture.h"
+using android::binder::unique_fd;
+
namespace android {
// Destructors need to be defined, even if pure virtual
@@ -75,7 +76,7 @@
auto port = trustyIpcPort(serverVersion);
int rc = connect(port.c_str(), IPC_CONNECT_WAIT_FOR_PORT);
LOG_ALWAYS_FATAL_IF(rc < 0, "Failed to connect to service: %d", rc);
- return base::unique_fd(rc);
+ return unique_fd(rc);
});
if (options.allowConnectFailure && status != OK) {
ret->sessions.clear();
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..41cb552 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>
@@ -40,6 +41,7 @@
#include <sys/prctl.h>
using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
+using android::binder::unique_fd;
namespace android {
namespace tests {
@@ -684,7 +686,7 @@
TEST_F(SafeInterfaceTest, TestIncrementNativeHandle) {
// Create an fd we can use to send and receive from the remote process
- base::unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)};
+ unique_fd eventFd{eventfd(0 /*initval*/, 0 /*flags*/)};
ASSERT_NE(-1, eventFd);
// Determine the maximum number of fds this process can have open
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
index b37030e..a648c08 100644
--- a/libs/binder/tests/binderTextOutputTest.cpp
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -20,13 +20,14 @@
#include <limits>
#include <cstddef>
-#include "android-base/file.h"
#include "android-base/test_utils.h"
#include <gtest/gtest.h>
#include <binder/Parcel.h>
#include <binder/TextOutput.h>
+#include "../file.h"
+
static void CheckMessage(CapturedStderr& cap,
const char* expected,
bool singleline) {
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..83db6c9 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -16,6 +16,9 @@
"parcelables/SingleDataParcelable.aidl",
"parcelables/GenericDataParcelable.aidl",
],
+ flags: [
+ "-Werror",
+ ],
backend: {
java: {
enabled: true,
@@ -32,7 +35,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..08fe071 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -25,11 +25,13 @@
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
#include <binder/Status.h>
+#include <utils/Flattenable.h>
#include "../../Utils.h"
-using ::android::status_t;
using ::android::HexString;
+using ::android::status_t;
+using ::android::binder::unique_fd;
enum ByteEnum : int8_t {};
enum IntEnum : int32_t {};
@@ -306,11 +308,12 @@
},
PARCEL_READ_NO_STATUS(int, readFileDescriptor),
PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
- PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor),
+ PARCEL_READ_WITH_STATUS(unique_fd, readUniqueFileDescriptor),
- PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
- PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
- PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
+ PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<unique_fd>>,
+ readUniqueFileDescriptorVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<unique_fd>>, readUniqueFileDescriptorVector),
+ PARCEL_READ_WITH_STATUS(std::vector<unique_fd>, readUniqueFileDescriptorVector),
[] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
size_t len = provider.ConsumeIntegral<size_t>();
diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
index c0fdaea..57521f4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-#include <android-base/file.h>
+#include "../../FileUtils.h"
+
#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
#include <binder/RecordedTransaction.h>
+#include <binder/unique_fd.h>
#include <fuzzseeds/random_parcel_seeds.h>
#include <sys/prctl.h>
+#include <sys/stat.h>
using android::generateSeedsFromRecording;
using android::status_t;
-using android::base::unique_fd;
+using android::binder::unique_fd;
using android::binder::debug::RecordedTransaction;
status_t generateCorpus(const char* recordingPath, const char* corpusDir) {
@@ -49,7 +51,7 @@
std::string filePath = std::string(corpusDir) + std::string("transaction_") +
std::to_string(transactionNumber);
constexpr int openFlags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
- android::base::unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666));
+ unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666));
if (!corpusFd.ok()) {
std::cerr << "Failed to open fd. Path " << filePath
<< " with error: " << strerror(errno) << std::endl;
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
index 6ea9708..8d1299d 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android-base/unique_fd.h>
+#include <binder/unique_fd.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <vector>
@@ -27,6 +27,6 @@
// get a random FD for use in fuzzing, of a few different specific types
//
// may return multiple FDs (e.g. pipe), but will always return at least one
-std::vector<base::unique_fd> getRandomFds(FuzzedDataProvider* provider);
+std::vector<binder::unique_fd> getRandomFds(FuzzedDataProvider* provider);
} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index 27587a9..2812da7 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -27,7 +27,7 @@
struct RandomParcelOptions {
std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader;
std::vector<sp<IBinder>> extraBinders;
- std::vector<base::unique_fd> extraFds;
+ std::vector<binder::unique_fd> extraFds;
};
/**
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
index 071250d..694b68d 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel_seeds/fuzzseeds/random_parcel_seeds.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include <android-base/file.h>
+#include "../../../../file.h"
+
#include <android-base/logging.h>
#include <binder/Binder.h>
@@ -40,6 +41,6 @@
template <typename T>
void writeReversedBuffer(std::vector<std::byte>& integralBuffer, T val);
} // namespace impl
-void generateSeedsFromRecording(base::borrowed_fd fd,
+void generateSeedsFromRecording(binder::borrowed_fd fd,
const binder::debug::RecordedTransaction& transaction);
} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 38e6f32..02e69cc 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -23,6 +23,8 @@
#include <private/android_filesystem_config.h>
+using android::binder::unique_fd;
+
namespace android {
void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
@@ -103,7 +105,7 @@
retBinders.end());
auto retFds = reply.debugReadAllFileDescriptors();
for (size_t i = 0; i < retFds.size(); i++) {
- options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
+ options.extraFds.push_back(unique_fd(dup(retFds[i])));
}
}
diff --git a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index dd08f72..9884dbb 100644
--- a/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -17,7 +17,7 @@
parcelable GenericDataParcelable {
enum JustSomeEnum {
- SOME_ENUMERATOR,
+ ONE_ENUMERATOR,
ANOTHER_ENUMERATOR,
MAYBE_ONE_MORE_ENUMERATOR,
}
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index 4a9bd07..c7d1533 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -23,7 +23,7 @@
namespace android {
-using base::unique_fd;
+using binder::unique_fd;
std::vector<unique_fd> getRandomFds(FuzzedDataProvider* provider) {
const char* fdType;
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index f367b41..4e58dc4 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -23,6 +23,8 @@
#include <fuzzbinder/random_fd.h>
#include <utils/String16.h>
+using android::binder::unique_fd;
+
namespace android {
static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
@@ -72,7 +74,7 @@
}
if (options->extraFds.size() > 0 && provider.ConsumeBool()) {
- const base::unique_fd& fd = options->extraFds.at(
+ const unique_fd& fd = options->extraFds.at(
provider.ConsumeIntegralInRange<size_t>(0,
options->extraFds.size() -
1));
@@ -83,7 +85,7 @@
return;
}
- std::vector<base::unique_fd> fds = getRandomFds(&provider);
+ std::vector<unique_fd> fds = getRandomFds(&provider);
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
true /*takeOwnership*/));
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
index 9e3e2ab..7b3c806 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-#include <android-base/file.h>
#include <android-base/logging.h>
#include <binder/RecordedTransaction.h>
#include <fuzzseeds/random_parcel_seeds.h>
-using android::base::WriteFully;
+#include "../../file.h"
+
+using android::binder::borrowed_fd;
+using android::binder::WriteFully;
namespace android {
namespace impl {
@@ -64,7 +66,7 @@
} // namespace impl
-void generateSeedsFromRecording(base::borrowed_fd fd,
+void generateSeedsFromRecording(borrowed_fd fd,
const binder::debug::RecordedTransaction& transaction) {
// Write Reserved bytes for future use
std::vector<uint8_t> reservedBytes(8);
diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp
index 71e847f..ab72bfd 100644
--- a/libs/binder/tests/rpc_fuzzer/Android.bp
+++ b/libs/binder/tests/rpc_fuzzer/Android.bp
@@ -25,13 +25,14 @@
"libbase",
"libcutils",
"liblog",
+ "libbinder_test_utils",
"libbinder_tls_static",
"libbinder_tls_test_utils",
"libssl_fuzz_unsafe",
"libcrypto_fuzz_unsafe",
],
cflags: [
- "-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE" // for RAND_reset_for_fuzzing
+ "-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE", // for RAND_reset_for_fuzzing
],
target: {
android: {
diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp
index dcc8b8e..50fc2f2 100644
--- a/libs/binder/tests/rpc_fuzzer/main.cpp
+++ b/libs/binder/tests/rpc_fuzzer/main.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <android-base/file.h>
+#include "../FileUtils.h"
+
#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
#include <binder/Binder.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
@@ -24,13 +24,18 @@
#include <binder/RpcTransport.h>
#include <binder/RpcTransportRaw.h>
#include <binder/RpcTransportTls.h>
+#include <binder/unique_fd.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/un.h>
+using android::binder::GetExecutableDirectory;
+using android::binder::unique_fd;
+
namespace android {
static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") +
@@ -76,12 +81,12 @@
ServerAuth readServerKeyAndCert() {
ServerAuth ret;
- auto keyPath = android::base::GetExecutableDirectory() + "/data/server.key";
+ auto keyPath = GetExecutableDirectory() + "/data/server.key";
bssl::UniquePtr<BIO> keyBio(BIO_new_file(keyPath.c_str(), "r"));
ret.pkey.reset(PEM_read_bio_PrivateKey(keyBio.get(), nullptr, passwordCallback, nullptr));
CHECK_NE(ret.pkey.get(), nullptr);
- auto certPath = android::base::GetExecutableDirectory() + "/data/server.crt";
+ auto certPath = GetExecutableDirectory() + "/data/server.crt";
bssl::UniquePtr<BIO> certBio(BIO_new_file(certPath.c_str(), "r"));
ret.cert.reset(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr));
CHECK_NE(ret.cert.get(), nullptr);
@@ -129,7 +134,7 @@
CHECK_LT(kSock.size(), sizeof(addr.sun_path));
memcpy(&addr.sun_path, kSock.c_str(), kSock.size());
- std::vector<base::unique_fd> connections;
+ std::vector<unique_fd> connections;
bool hangupBeforeShutdown = provider.ConsumeBool();
@@ -140,7 +145,7 @@
while (provider.remaining_bytes() > 0) {
if (connections.empty() ||
(connections.size() < kMaxConnections && provider.ConsumeBool())) {
- base::unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ unique_fd fd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
CHECK_NE(fd.get(), -1);
CHECK_EQ(0,
TEMP_FAILURE_RETRY(
diff --git a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
index 8d2b714..993418a 100644
--- a/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h
@@ -74,7 +74,7 @@
bbinder->getDebugPid();
},
[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
- (void)bbinder->setRpcClientDebug(android::base::unique_fd(),
+ (void)bbinder->setRpcClientDebug(binder::unique_fd(),
sp<BBinder>::make());
}};
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
index e494366..87b0fb6 100644
--- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -14,19 +14,20 @@
* limitations under the License.
*/
-#include <android-base/macros.h>
#include <binder/RecordedTransaction.h>
#include <filesystem>
#include "fuzzer/FuzzedDataProvider.h"
+using android::binder::unique_fd;
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::FILE* intermediateFile = std::tmpfile();
fwrite(data, sizeof(uint8_t), size, intermediateFile);
rewind(intermediateFile);
int fileNumber = fileno(intermediateFile);
- android::base::unique_fd fd(dup(fileNumber));
+ unique_fd fd(dup(fileNumber));
auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
@@ -35,8 +36,8 @@
if (transaction.has_value()) {
intermediateFile = std::tmpfile();
- android::base::unique_fd fdForWriting(fileno(intermediateFile));
- auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+ 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..fa939e6 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>
@@ -23,6 +22,7 @@
#include "fuzzer/FuzzedDataProvider.h"
using android::fillRandomParcel;
+using android::binder::unique_fd;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider provider = FuzzedDataProvider(data, size);
@@ -54,8 +54,8 @@
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);
+ 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/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
index 5e3502a..fe09978 100644
--- a/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
+++ b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
+#include "../../file.h"
+
#include <fuzzer/FuzzedDataProvider.h>
#include <binder/Parcel.h>
#include <binder/TextOutput.h>
-#include "android-base/file.h"
#include "android-base/test_utils.h"
#include <fcntl.h>
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 43e06e0..a8dabc3 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -22,17 +22,34 @@
#endif
#include <binder/RpcTransportTipcTrusty.h>
+#include <log/log.h>
+#include <trusty_log.h>
#include "../OS.h"
#include "TrustyStatus.h"
-using android::base::Result;
+#include <cstdarg>
+
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
namespace android::binder::os {
-Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) {
+void trace_begin(uint64_t, const char*) {}
+
+void trace_end(uint64_t) {}
+
+uint64_t GetThreadId() {
+ return 0;
+}
+
+bool report_sysprop_change() {
+ return false;
+}
+
+status_t setNonBlocking(borrowed_fd /*fd*/) {
// Trusty IPC syscalls are all non-blocking by default.
- return {};
+ return OK;
}
status_t getRandomBytes(uint8_t* data, size_t size) {
@@ -61,16 +78,51 @@
ssize_t sendMessageOnSocket(
const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
- const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* /* ancillaryFds */) {
errno = ENOTSUP;
return -1;
}
ssize_t receiveMessageFromSocket(
const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
- std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /* ancillaryFds */) {
+ std::vector<std::variant<unique_fd, borrowed_fd>>* /* ancillaryFds */) {
errno = ENOTSUP;
return -1;
}
} // namespace android::binder::os
+
+int __android_log_print(int prio [[maybe_unused]], const char* tag, const char* fmt, ...) {
+#ifdef TRUSTY_USERSPACE
+#define trusty_tlog _tlog
+#define trusty_vtlog _vtlog
+#else
+ // mapping taken from kernel trusty_log.h (TLOGx)
+ int kernelLogLevel;
+ if (prio <= ANDROID_LOG_DEBUG) {
+ kernelLogLevel = LK_DEBUGLEVEL_ALWAYS;
+ } else if (prio == ANDROID_LOG_INFO) {
+ kernelLogLevel = LK_DEBUGLEVEL_SPEW;
+ } else if (prio == ANDROID_LOG_WARN) {
+ kernelLogLevel = LK_DEBUGLEVEL_INFO;
+ } else if (prio == ANDROID_LOG_ERROR) {
+ kernelLogLevel = LK_DEBUGLEVEL_CRITICAL;
+ } else { /* prio >= ANDROID_LOG_FATAL */
+ kernelLogLevel = LK_DEBUGLEVEL_CRITICAL;
+ }
+#if LK_DEBUGLEVEL_NO_ALIASES
+ auto LK_DEBUGLEVEL_kernelLogLevel = kernelLogLevel;
+#endif
+
+#define trusty_tlog(...) _tlog(kernelLogLevel, __VA_ARGS__)
+#define trusty_vtlog(...) _vtlog(kernelLogLevel, __VA_ARGS__)
+#endif
+
+ va_list args;
+ va_start(args, fmt);
+ trusty_tlog((tag[0] == '\0') ? "libbinder" : "libbinder-");
+ trusty_vtlog(fmt, args);
+ va_end(args);
+
+ return 1;
+}
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 8f64323..1f857a0 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -27,11 +27,11 @@
#include "../RpcState.h"
#include "TrustyStatus.h"
-using android::base::unexpected;
+using android::binder::unique_fd;
namespace android {
-android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make(
+sp<RpcServerTrusty> RpcServerTrusty::make(
tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
// Default is without TLS.
@@ -39,18 +39,21 @@
rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make();
auto ctx = rpcTransportCtxFactory->newServerCtx();
if (ctx == nullptr) {
- return unexpected(ERR_NO_MEMORY);
+ ALOGE("Failed to create RpcServerTrusty: can't create server context");
+ return nullptr;
}
auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl),
msgMaxSize);
if (srv == nullptr) {
- return unexpected(ERR_NO_MEMORY);
+ ALOGE("Failed to create RpcServerTrusty: can't create server object");
+ return nullptr;
}
int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps);
if (rc != NO_ERROR) {
- return unexpected(rc);
+ ALOGE("Failed to create RpcServerTrusty: can't add service: %d", rc);
+ return nullptr;
}
return srv;
}
@@ -129,7 +132,7 @@
if (chanDup < 0) {
return chanDup;
}
- base::unique_fd clientFd(chanDup);
+ unique_fd clientFd(chanDup);
android::RpcTransportFd transportFd(std::move(clientFd));
std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 692f82d..c74ba0a 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -29,6 +29,10 @@
namespace android {
+using namespace android::binder::impl;
+using android::binder::borrowed_fd;
+using android::binder::unique_fd;
+
// RpcTransport for Trusty.
class RpcTransportTipcTrusty : public RpcTransport {
public:
@@ -45,9 +49,8 @@
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)
- override {
+ const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
+ const std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
if (niovs < 0) {
return BAD_VALUE;
}
@@ -115,8 +118,8 @@
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) override {
+ const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
+ std::vector<std::variant<unique_fd, borrowed_fd>>* ancillaryFds) override {
if (niovs < 0) {
return BAD_VALUE;
}
@@ -168,7 +171,7 @@
if (ancillaryFds != nullptr) {
ancillaryFds->reserve(ancillaryFds->size() + mMessageInfo.num_handles);
for (size_t i = 0; i < mMessageInfo.num_handles; i++) {
- ancillaryFds->emplace_back(base::unique_fd(msgHandles[i]));
+ ancillaryFds->emplace_back(unique_fd(msgHandles[i]));
}
// Clear the saved number of handles so we don't accidentally
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/manifest.json b/libs/binder/trusty/binderRpcTest/service/manifest.json
index 1c4f7ee..d2a1fc0 100644
--- a/libs/binder/trusty/binderRpcTest/service/manifest.json
+++ b/libs/binder/trusty/binderRpcTest/service/manifest.json
@@ -2,7 +2,7 @@
"uuid": "87e424e5-69d7-4bbd-8b7c-7e24812cbc94",
"app_name": "binderRpcTestService",
"min_heap": 65536,
- "min_stack": 16384,
+ "min_stack": 20480,
"mgmt_flags": {
"restart_on_exit": true,
"non_critical_app": true
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/binder_rpc_unstable/rules.mk b/libs/binder/trusty/binder_rpc_unstable/rules.mk
new file mode 100644
index 0000000..d8dbce5
--- /dev/null
+++ b/libs/binder/trusty/binder_rpc_unstable/rules.mk
@@ -0,0 +1,32 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := \
+ $(LIBBINDER_DIR)/libbinder_rpc_unstable.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+ $(LIBBINDER_DIR)/include_rpc_unstable \
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ trusty/user/base/lib/libstdc++-trusty \
+
+include make/library.mk
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 8924b36..f35d6c2 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -16,13 +16,11 @@
#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>
#include <binder/RpcSession.h>
#include <binder/RpcTransport.h>
+#include <binder/unique_fd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -54,7 +52,7 @@
* The caller is responsible for calling tipc_run_event_loop() to start
* the TIPC event loop after creating one or more services here.
*/
- static android::base::expected<sp<RpcServerTrusty>, int> make(
+ static sp<RpcServerTrusty> make(
tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
size_t msgMaxSize,
std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
@@ -83,7 +81,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/include/log/log.h b/libs/binder/trusty/include/log/log.h
deleted file mode 100644
index de84617..0000000
--- a/libs/binder/trusty/include/log/log.h
+++ /dev/null
@@ -1,128 +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.
- */
-
-#pragma once
-
-#define BINDER_LOG_LEVEL_NONE 0
-#define BINDER_LOG_LEVEL_NORMAL 1
-#define BINDER_LOG_LEVEL_VERBOSE 2
-
-#ifndef BINDER_LOG_LEVEL
-#define BINDER_LOG_LEVEL BINDER_LOG_LEVEL_NORMAL
-#endif // BINDER_LOG_LEVEL
-
-#ifndef TLOG_TAG
-#ifdef LOG_TAG
-#define TLOG_TAG "libbinder-" LOG_TAG
-#else // LOG_TAG
-#define TLOG_TAG "libbinder"
-#endif // LOG_TAG
-#endif // TLOG_TAG
-
-#include <stdlib.h>
-#include <trusty_log.h>
-
-static inline void __ignore_va_args__(...) {}
-
-#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
-#define ALOGD(fmt, ...) TLOGD(fmt "\n", ##__VA_ARGS__)
-#define ALOGI(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
-#define ALOGW(fmt, ...) TLOGW(fmt "\n", ##__VA_ARGS__)
-#define ALOGE(fmt, ...) TLOGE(fmt "\n", ##__VA_ARGS__)
-#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
-#define ALOGD(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#define ALOGI(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#define ALOGW(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#define ALOGE(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_NORMAL
-
-#if BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
-#define IF_ALOGV() if (TLOG_LVL >= TLOG_LVL_INFO)
-#define ALOGV(fmt, ...) TLOGI(fmt "\n", ##__VA_ARGS__)
-#else // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
-#define IF_ALOGV() if (false)
-#define ALOGV(fmt, ...) \
- while (0) { \
- __ignore_va_args__(__VA_ARGS__); \
- }
-#endif // BINDER_LOG_LEVEL >= BINDER_LOG_LEVEL_VERBOSE
-
-#define ALOGI_IF(cond, ...) \
- do { \
- if (cond) { \
- ALOGI(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-#define ALOGE_IF(cond, ...) \
- do { \
- if (cond) { \
- ALOGE(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-#define ALOGW_IF(cond, ...) \
- do { \
- if (cond) { \
- ALOGW(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-
-#define LOG_ALWAYS_FATAL(fmt, ...) \
- do { \
- TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
- abort(); \
- } while (0)
-#define LOG_ALWAYS_FATAL_IF(cond, ...) \
- do { \
- if (cond) { \
- LOG_ALWAYS_FATAL(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-#define LOG_FATAL(fmt, ...) \
- do { \
- TLOGE("libbinder fatal error: " fmt "\n", ##__VA_ARGS__); \
- abort(); \
- } while (0)
-#define LOG_FATAL_IF(cond, ...) \
- do { \
- if (cond) { \
- LOG_FATAL(#cond ": " __VA_ARGS__); \
- } \
- } while (0)
-
-#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__)
-
-#define android_errorWriteLog(tag, subTag) \
- do { \
- TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
- } while (0)
-
-// Override the definition of __assert from binder_status.h
-#ifndef __BIONIC__
-#undef __assert
-#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
-#endif // __BIONIC__
diff --git a/libs/binder/trusty/include_mock/trusty_log.h b/libs/binder/trusty/include_mock/trusty_log.h
index d51e752..9aa9031 100644
--- a/libs/binder/trusty/include_mock/trusty_log.h
+++ b/libs/binder/trusty/include_mock/trusty_log.h
@@ -24,3 +24,6 @@
#define TLOGW(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define TLOGE(fmt, ...) printf(fmt, ##__VA_ARGS__)
#define TLOGC(fmt, ...) printf(fmt, ##__VA_ARGS__)
+
+#define _tlog(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#define _vtlog(fmt, args) vprintf(fmt, args)
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 1f05ef7..5cbe0af 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -18,35 +18,32 @@
MODULE := $(LOCAL_DIR)
LIBBINDER_DIR := frameworks/native/libs/binder
+# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release
LIBBASE_DIR := system/libbase
-LIBCUTILS_DIR := system/core/libcutils
-LIBUTILS_DIR := system/core/libutils
+LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub
+LIBUTILS_BINDER_DIR := system/core/libutils/binder
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
- $(LOCAL_DIR)/../logging.cpp \
+ $(LOCAL_DIR)/../OS.cpp \
$(LOCAL_DIR)/../TrustyStatus.cpp \
$(LIBBINDER_DIR)/Binder.cpp \
$(LIBBINDER_DIR)/BpBinder.cpp \
$(LIBBINDER_DIR)/FdTrigger.cpp \
$(LIBBINDER_DIR)/IInterface.cpp \
$(LIBBINDER_DIR)/IResultReceiver.cpp \
- $(LIBBINDER_DIR)/OS_android.cpp \
$(LIBBINDER_DIR)/Parcel.cpp \
$(LIBBINDER_DIR)/Stability.cpp \
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
- $(LIBBASE_DIR)/hex.cpp \
- $(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/binder/Errors.cpp \
- $(LIBUTILS_DIR)/binder/RefBase.cpp \
- $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/binder/String16.cpp \
- $(LIBUTILS_DIR)/binder/String8.cpp \
- $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
- $(LIBUTILS_DIR)/binder/Unicode.cpp \
- $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
- $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBUTILS_BINDER_DIR)/Errors.cpp \
+ $(LIBUTILS_BINDER_DIR)/RefBase.cpp \
+ $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_BINDER_DIR)/String16.cpp \
+ $(LIBUTILS_BINDER_DIR)/String8.cpp \
+ $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_BINDER_DIR)/Unicode.cpp \
+ $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \
MODULE_DEFINES += \
LK_DEBUGLEVEL_NO_ALIASES=1 \
@@ -57,17 +54,22 @@
GLOBAL_INCLUDES += \
$(LOCAL_DIR)/include \
$(LOCAL_DIR)/../include \
+ $(LIBLOG_STUB_DIR)/include \
$(LIBBINDER_DIR)/include \
$(LIBBINDER_DIR)/ndk/include_cpp \
$(LIBBASE_DIR)/include \
- $(LIBCUTILS_DIR)/include \
- $(LIBUTILS_DIR)/include \
+ $(LIBUTILS_BINDER_DIR)/include \
$(FMTLIB_DIR)/include \
GLOBAL_COMPILEFLAGS += \
-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \
-DBINDER_NO_KERNEL_IPC \
-DBINDER_RPC_SINGLE_THREADED \
+ -DBINDER_ENABLE_LIBLOG_ASSERT \
+ -DBINDER_DISABLE_NATIVE_HANDLE \
+ -DBINDER_DISABLE_BLOB \
+ -DBINDER_NO_LIBBASE \
+ -D__ANDROID_VENDOR__ \
-D__ANDROID_VNDK__ \
MODULE_DEPS += \
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..f2f140d 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -18,13 +18,13 @@
MODULE := $(LOCAL_DIR)
LIBBINDER_DIR := frameworks/native/libs/binder
+# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release
LIBBASE_DIR := system/libbase
-LIBCUTILS_DIR := system/core/libcutils
-LIBUTILS_DIR := system/core/libutils
+LIBLOG_STUB_DIR := $(LIBBINDER_DIR)/liblog_stub
+LIBUTILS_BINDER_DIR := system/core/libutils/binder
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
- $(LOCAL_DIR)/logging.cpp \
$(LOCAL_DIR)/OS.cpp \
$(LOCAL_DIR)/RpcServerTrusty.cpp \
$(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \
@@ -35,7 +35,6 @@
$(LIBBINDER_DIR)/FdTrigger.cpp \
$(LIBBINDER_DIR)/IInterface.cpp \
$(LIBBINDER_DIR)/IResultReceiver.cpp \
- $(LIBBINDER_DIR)/OS_android.cpp \
$(LIBBINDER_DIR)/Parcel.cpp \
$(LIBBINDER_DIR)/ParcelFileDescriptor.cpp \
$(LIBBINDER_DIR)/RpcServer.cpp \
@@ -44,24 +43,22 @@
$(LIBBINDER_DIR)/Stability.cpp \
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
- $(LIBBASE_DIR)/hex.cpp \
- $(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/binder/Errors.cpp \
- $(LIBUTILS_DIR)/binder/RefBase.cpp \
- $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/binder/String16.cpp \
- $(LIBUTILS_DIR)/binder/String8.cpp \
- $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
- $(LIBUTILS_DIR)/binder/Unicode.cpp \
- $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
- $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBBINDER_DIR)/file.cpp \
+ $(LIBUTILS_BINDER_DIR)/Errors.cpp \
+ $(LIBUTILS_BINDER_DIR)/RefBase.cpp \
+ $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_BINDER_DIR)/String16.cpp \
+ $(LIBUTILS_BINDER_DIR)/String8.cpp \
+ $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_BINDER_DIR)/Unicode.cpp \
+ $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \
MODULE_EXPORT_INCLUDES += \
$(LOCAL_DIR)/include \
+ $(LIBLOG_STUB_DIR)/include \
$(LIBBINDER_DIR)/include \
$(LIBBASE_DIR)/include \
- $(LIBCUTILS_DIR)/include \
- $(LIBUTILS_DIR)/include \
+ $(LIBUTILS_BINDER_DIR)/include \
$(FMTLIB_DIR)/include \
# The android/binder_to_string.h header is shared between libbinder and
@@ -71,6 +68,11 @@
MODULE_EXPORT_COMPILEFLAGS += \
-DBINDER_RPC_SINGLE_THREADED \
+ -DBINDER_ENABLE_LIBLOG_ASSERT \
+ -DBINDER_DISABLE_NATIVE_HANDLE \
+ -DBINDER_DISABLE_BLOB \
+ -DBINDER_NO_LIBBASE \
+ -D__ANDROID_VENDOR__ \
-D__ANDROID_VNDK__ \
# libbinder has some deprecated declarations that we want to produce warnings
diff --git a/libs/gui/include/gui/Flags.h b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp
similarity index 71%
copy from libs/gui/include/gui/Flags.h
copy to libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp
index a2cff56..6f96566 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/BinderBindings.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * 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.
@@ -14,9 +14,4 @@
* 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
+#include <binder_rpc_unstable.hpp>
diff --git a/libs/gui/include/gui/Flags.h b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs
similarity index 71%
copy from libs/gui/include/gui/Flags.h
copy to libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs
index a2cff56..c7036f4 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/lib.rs
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * 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.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#pragma once
+//! Generated Rust bindings to binder_rpc_unstable
-// 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
+#[allow(bad_style)]
+mod sys {
+ include!(env!("BINDGEN_INC_FILE"));
+}
+
+pub use sys::*;
diff --git a/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
new file mode 100644
index 0000000..ef1b7c3
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_unstable_bindgen/rules.mk
@@ -0,0 +1,40 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/lib.rs
+
+MODULE_CRATE_NAME := binder_rpc_unstable_bindgen
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/binder_rpc_unstable \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
+ trusty/user/base/lib/libstdc++-trusty \
+ trusty/user/base/lib/trusty-sys \
+
+MODULE_BINDGEN_SRC_HEADER := $(LOCAL_DIR)/BinderBindings.hpp
+
+MODULE_BINDGEN_FLAGS += \
+ --blocklist-type="AIBinder" \
+ --raw-line="use binder_ndk_sys::AIBinder;" \
+ --rustified-enum="ARpcSession_FileDescriptorTransportMode" \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk
new file mode 100644
index 0000000..76f3b94
--- /dev/null
+++ b/libs/binder/trusty/rust/rpcbinder/rules.mk
@@ -0,0 +1,35 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LIBBINDER_DIR)/rust/rpcbinder/src/lib.rs
+
+MODULE_CRATE_NAME := rpcbinder
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ $(LIBBINDER_DIR)/trusty/rust \
+ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
+ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
+ external/rust/crates/foreign-types \
+ trusty/user/base/lib/tipc/rust \
+ trusty/user/base/lib/trusty-sys \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
new file mode 100644
index 0000000..d343f14
--- /dev/null
+++ b/libs/binder/trusty/rust/rules.mk
@@ -0,0 +1,41 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LIBBINDER_DIR)/rust/src/lib.rs
+
+MODULE_CRATE_NAME := binder
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
+ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
+ external/rust/crates/downcast-rs \
+ trusty/user/base/lib/trusty-sys \
+
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+
+# Trusty does not have `ProcessState`, so there are a few
+# doc links in `IBinder` that are still broken.
+MODULE_RUSTFLAGS += \
+ --allow rustdoc::broken-intra-doc-links \
+
+include make/library.mk
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..bb573c5 100644
--- a/libs/bufferstreams/examples/app/Android.bp
+++ b/libs/bufferstreams/examples/app/Android.bp
@@ -14,14 +14,36 @@
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",
+ ],
+ optimize: {
+ proguard_flags_files: ["proguard-rules.pro"],
+ },
+
+ 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..ede7793
--- /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(): String;
+ external fun testBufferQueueCreation();
+
+ companion object {
+ fun companion_stringFromJNI(): String {
+ val instance = BufferStreamJNI()
+ return instance.stringFromJNI()
+ }
+
+ fun companion_testBufferQueueCreation() {
+ val instance = BufferStreamJNI()
+ return 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..95e415e
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/DemoScreen1.kt
@@ -0,0 +1,35 @@
+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.padding
+import androidx.compose.material3.Button
+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) {
+ LogOutput.getInstance().LogOutputComposable()
+ 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 = { LogOutput.getInstance().clearText() }) {
+ Text("Clear")
+ }
+ }
+ }
+ }
+}
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/LogOutput.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt
new file mode 100644
index 0000000..3f0926f
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/LogOutput.kt
@@ -0,0 +1,65 @@
+package com.android.graphics.bufferstreamsdemoapp
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Card
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import java.util.Collections
+
+/*
+LogOutput centralizes logging: storing, displaying, adding, and clearing log messages with
+thread safety. It is a singleton that's also accessed from C++. The private constructor will
+not allow this class to be initialized, limiting it to getInstance().
+ */
+class LogOutput private constructor() {
+ val logs = Collections.synchronizedList(mutableStateListOf<String>())
+
+ @Composable
+ fun LogOutputComposable() {
+ val rlogs = remember { logs }
+
+ Card(modifier = Modifier.fillMaxWidth().padding(16.dp).height(400.dp)) {
+ Column(
+ modifier =
+ Modifier.padding(10.dp).size(380.dp).verticalScroll(rememberScrollState())) {
+ for (log in rlogs) {
+ Text(log, modifier = Modifier.padding(0.dp))
+ }
+ }
+ }
+ }
+
+ fun clearText() {
+ logs.clear()
+ }
+
+ fun addLog(log: String) {
+ logs.add(log)
+ }
+
+ companion object {
+ @Volatile private var instance: LogOutput? = null
+
+ @JvmStatic
+ fun getInstance(): LogOutput {
+ if (instance == null) {
+ synchronized(this) {
+ if (instance == null) {
+ instance = LogOutput()
+ }
+ }
+ }
+ return instance!!
+ }
+ }
+}
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..2ccd8d7
--- /dev/null
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/MainActivity.kt
@@ -0,0 +1,132 @@
+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
+import java.util.*
+
+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..550ad22 100644
--- a/libs/bufferstreams/examples/app/jni/main.cpp
+++ b/libs/bufferstreams/examples/app/jni/main.cpp
@@ -13,25 +13,41 @@
// limitations under the License.
#include <jni.h>
+#include <string>
#include <gui/BufferQueue.h>
-extern "C"
-{
- JNIEXPORT jstring JNICALL
- Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_stringFromJNI(
- JNIEnv *env,
- jobject /* this */) {
- const char* hello = "Hello from C++";
- return env->NewStringUTF(hello);
- }
+void log(JNIEnv* env, std::string l) {
+ jclass clazz = env->FindClass("com/android/graphics/bufferstreamsdemoapp/LogOutput");
+ jmethodID getInstance = env->GetStaticMethodID(clazz, "getInstance",
+ "()Lcom/android/graphics/bufferstreamsdemoapp/LogOutput;");
+ jmethodID addLog = env->GetMethodID(clazz, "addLog", "(Ljava/lang/String;)V");
+ jobject dmg = env->CallStaticObjectMethod(clazz, getInstance);
- JNIEXPORT void JNICALL
- Java_com_android_graphics_bufferstreamsdemoapp_MainActivity_RunBufferQueue(
- JNIEnv *env,
- jobject /* this */) {
- android::sp<android::IGraphicBufferProducer> producer;
- android::sp<android::IGraphicBufferConsumer> consumer;
- android::BufferQueue::createBufferQueue(&producer, &consumer);
- }
+ jstring jlog = env->NewStringUTF(l.c_str());
+ env->CallVoidMethod(dmg, addLog, jlog);
+}
+
+extern "C" {
+
+JNIEXPORT jstring JNICALL
+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_BufferStreamJNI_testBufferQueueCreation(
+ JNIEnv* env, jobject /* thiz */) {
+
+ log(env, "Calling testBufferQueueCreation.");
+ android::sp<android::IGraphicBufferProducer> producer;
+ log(env, "Created producer.");
+ android::sp<android::IGraphicBufferConsumer> consumer;
+ log(env, "Created consumer.");
+ android::BufferQueue::createBufferQueue(&producer, &consumer);
+ log(env, "Created BufferQueue successfully.");
+ log(env, "Done!");
+}
}
\ No newline at end of file
diff --git a/libs/bufferstreams/examples/app/proguard-rules.pro b/libs/bufferstreams/examples/app/proguard-rules.pro
new file mode 100644
index 0000000..7a987fc
--- /dev/null
+++ b/libs/bufferstreams/examples/app/proguard-rules.pro
@@ -0,0 +1,23 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-keep,allowoptimization,allowobfuscation class com.android.graphics.bufferstreamsdemoapp.** { *; }
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/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
index ae242f3..08f30de 100644
--- a/libs/fakeservicemanager/FakeServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -122,9 +122,19 @@
}
void FakeServiceManager::clear() {
- std::lock_guard<std::mutex> l(mMutex);
+ std::map<String16, sp<IBinder>> backup;
- mNameToService.clear();
+ {
+ std::lock_guard<std::mutex> l(mMutex);
+ backup = mNameToService;
+ mNameToService.clear();
+ }
+
+ // destructors may access FSM, so avoid recursive lock
+ backup.clear(); // explicit
+
+ // TODO: destructors may have added more services here - may want
+ // to check this or abort
}
} // namespace android
@@ -147,4 +157,4 @@
LOG_ALWAYS_FATAL_IF(gFakeServiceManager == nullptr, "Fake Service Manager is not available. Forgot to call setupFakeServiceManager?");
gFakeServiceManager->clear();
}
-} //extern "C"
\ No newline at end of file
+} //extern "C"
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index ea1b5e4..918680d 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -17,6 +17,7 @@
"enum_test.cpp",
"fake_guard_test.cpp",
"flags_test.cpp",
+ "function_test.cpp",
"future_test.cpp",
"match_test.cpp",
"mixins_test.cpp",
diff --git a/libs/ftl/function_test.cpp b/libs/ftl/function_test.cpp
new file mode 100644
index 0000000..91b5e08
--- /dev/null
+++ b/libs/ftl/function_test.cpp
@@ -0,0 +1,379 @@
+/*
+ * Copyright 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.
+ */
+
+#include <ftl/function.h>
+#include <gtest/gtest.h>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <type_traits>
+
+namespace android::test {
+namespace {
+
+// Create an alias to composite requirements defined by the trait class `T` for easier testing.
+template <typename T, typename S>
+inline constexpr bool is_opaquely_storable = (T::template require_trivially_copyable<S> &&
+ T::template require_trivially_destructible<S> &&
+ T::template require_will_fit_in_opaque_storage<S> &&
+ T::template require_alignment_compatible<S>);
+
+// `I` gives a count of sizeof(std::intptr_t) bytes , and `J` gives a raw count of bytes
+template <size_t I, size_t J = 0>
+struct KnownSizeFunctionObject {
+ using Data = std::array<std::byte, sizeof(std::intptr_t) * I + J>;
+ void operator()() const {};
+ Data data{};
+};
+
+} // namespace
+
+// static_assert the expected type traits
+static_assert(std::is_invocable_r_v<void, ftl::Function<void()>>);
+static_assert(std::is_trivially_copyable_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_destructible_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_copy_constructible_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_move_constructible_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_copy_assignable_v<ftl::Function<void()>>);
+static_assert(std::is_trivially_move_assignable_v<ftl::Function<void()>>);
+
+template <typename T>
+using function_traits = ftl::details::function_traits<T>;
+
+// static_assert that the expected value of N is used for known function object sizes.
+static_assert(function_traits<KnownSizeFunctionObject<0, 0>>::size == 0);
+static_assert(function_traits<KnownSizeFunctionObject<0, 1>>::size == 0);
+static_assert(function_traits<KnownSizeFunctionObject<1, 0>>::size == 0);
+static_assert(function_traits<KnownSizeFunctionObject<1, 1>>::size == 1);
+static_assert(function_traits<KnownSizeFunctionObject<2, 0>>::size == 1);
+static_assert(function_traits<KnownSizeFunctionObject<2, 1>>::size == 2);
+
+// Check that is_function_v works
+static_assert(!ftl::is_function_v<KnownSizeFunctionObject<0>>);
+static_assert(!ftl::is_function_v<std::function<void()>>);
+static_assert(ftl::is_function_v<ftl::Function<void()>>);
+
+// static_assert what can and cannot be stored inside the opaque storage
+
+template <size_t N>
+using function_opaque_storage = ftl::details::function_opaque_storage<N>;
+
+// Function objects can be stored if they fit.
+static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<0>>);
+static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<1>>);
+static_assert(!is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<2>>);
+
+static_assert(is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<2>>);
+static_assert(!is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<3>>);
+
+static_assert(is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<3>>);
+static_assert(!is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<4>>);
+
+// Another opaque storage can be stored if it fits. This property is used to copy smaller
+// ftl::Functions into larger ones.
+static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<0>::type>);
+static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<1>::type>);
+static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<2>::type>);
+static_assert(!is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<3>::type>);
+
+// Function objects that aren't trivially copyable or destroyable cannot be stored.
+auto lambda_capturing_unique_ptr = [ptr = std::unique_ptr<void*>()] { static_cast<void>(ptr); };
+static_assert(
+ !is_opaquely_storable<function_opaque_storage<2>, decltype(lambda_capturing_unique_ptr)>);
+
+// Keep in sync with "Example usage" in header file.
+TEST(Function, Example) {
+ using namespace std::string_view_literals;
+
+ class MyClass {
+ public:
+ void on_event() const {}
+ int on_string(int*, std::string_view) { return 1; }
+
+ auto get_function() {
+ return ftl::make_function([this] { on_event(); });
+ }
+ } cls;
+
+ // A function container with no arguments, and returning no value.
+ ftl::Function<void()> f;
+
+ // Construct a ftl::Function containing a small lambda.
+ f = cls.get_function();
+
+ // Construct a ftl::Function that calls `cls.on_event()`.
+ f = ftl::make_function<&MyClass::on_event>(&cls);
+
+ // Create a do-nothing function.
+ f = ftl::no_op;
+
+ // Invoke the contained function.
+ f();
+
+ // Also invokes it.
+ std::invoke(f);
+
+ // Create a typedef to give a more meaningful name and bound the size.
+ using MyFunction = ftl::Function<int(std::string_view), 2>;
+ int* ptr = nullptr;
+ auto f1 =
+ MyFunction::make([cls = &cls, ptr](std::string_view sv) { return cls->on_string(ptr, sv); });
+ int r = f1("abc"sv);
+
+ // Returns a default-constructed int (0).
+ f1 = ftl::no_op;
+ r = f1("abc"sv);
+ EXPECT_EQ(r, 0);
+}
+
+TEST(Function, BasicOperations) {
+ // Default constructible.
+ ftl::Function<int()> f;
+
+ // Compares as empty
+ EXPECT_FALSE(f);
+ EXPECT_TRUE(f == nullptr);
+ EXPECT_FALSE(f != nullptr);
+ EXPECT_TRUE(ftl::Function<int()>() == f);
+ EXPECT_FALSE(ftl::Function<int()>() != f);
+
+ // Assigning no_op sets it to not empty.
+ f = ftl::no_op;
+
+ // Verify it can be called, and that it returns a default constructed value.
+ EXPECT_EQ(f(), 0);
+
+ // Comparable when non-empty.
+ EXPECT_TRUE(f);
+ EXPECT_FALSE(f == nullptr);
+ EXPECT_TRUE(f != nullptr);
+ EXPECT_FALSE(ftl::Function<int()>() == f);
+ EXPECT_TRUE(ftl::Function<int()>() != f);
+
+ // Constructing from nullptr means empty.
+ f = ftl::Function<int()>{nullptr};
+ EXPECT_FALSE(f);
+
+ // Assigning nullptr means it is empty.
+ f = nullptr;
+ EXPECT_FALSE(f);
+
+ // Move construction
+ f = ftl::no_op;
+ ftl::Function<int()> g{std::move(f)};
+ EXPECT_TRUE(g != nullptr);
+
+ // Move assignment
+ f = nullptr;
+ f = std::move(g);
+ EXPECT_TRUE(f != nullptr);
+
+ // Copy construction
+ ftl::Function<int()> h{f};
+ EXPECT_TRUE(h != nullptr);
+
+ // Copy assignment
+ g = h;
+ EXPECT_TRUE(g != nullptr);
+}
+
+TEST(Function, CanMoveConstructFromLambda) {
+ auto lambda = [] {};
+ ftl::Function<void()> f{std::move(lambda)};
+}
+
+TEST(Function, TerseDeducedConstructAndAssignFromLambda) {
+ auto f = ftl::Function([] { return 1; });
+ EXPECT_EQ(f(), 1);
+
+ f = [] { return 2; };
+ EXPECT_EQ(f(), 2);
+}
+
+namespace {
+
+struct ImplicitConversionsHelper {
+ auto exact(int) -> int { return 0; }
+ auto inexact(long) -> short { return 0; }
+ // TODO: Switch to `auto templated(auto x)` with C++20
+ template <typename T>
+ T templated(T x) {
+ return x;
+ }
+
+ static auto static_exact(int) -> int { return 0; }
+ static auto static_inexact(long) -> short { return 0; }
+ // TODO: Switch to `static auto static_templated(auto x)` with C++20
+ template <typename T>
+ static T static_templated(T x) {
+ return x;
+ }
+};
+
+} // namespace
+
+TEST(Function, ImplicitConversions) {
+ using Function = ftl::Function<int(int)>;
+ auto check = [](Function f) { return f(0); };
+ auto exact = [](int) -> int { return 0; };
+ auto inexact = [](long) -> short { return 0; };
+ auto templated = [](auto x) { return x; };
+
+ ImplicitConversionsHelper helper;
+
+ // Note, `check(nullptr)` would crash, so we can only check if it would be invocable.
+ static_assert(std::is_invocable_v<decltype(check), decltype(nullptr)>);
+
+ // Note: We invoke each of these to fully expand all the templates involved.
+ EXPECT_EQ(check(ftl::no_op), 0);
+
+ EXPECT_EQ(check(exact), 0);
+ EXPECT_EQ(check(inexact), 0);
+ EXPECT_EQ(check(templated), 0);
+
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::exact>(&helper)), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::inexact>(&helper)), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::templated<int>>(&helper)), 0);
+
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_exact>()), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_inexact>()), 0);
+ EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_templated<int>>()), 0);
+}
+
+TEST(Function, MakeWithNonConstMemberFunction) {
+ struct Observer {
+ bool called = false;
+ void setCalled() { called = true; }
+ } observer;
+
+ auto f = ftl::make_function<&Observer::setCalled>(&observer);
+
+ f();
+
+ EXPECT_TRUE(observer.called);
+
+ EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
+}
+
+TEST(Function, MakeWithConstMemberFunction) {
+ struct Observer {
+ mutable bool called = false;
+ void setCalled() const { called = true; }
+ } observer;
+
+ const auto f = ftl::make_function<&Observer::setCalled>(&observer);
+
+ f();
+
+ EXPECT_TRUE(observer.called);
+
+ EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
+}
+
+TEST(Function, MakeWithConstClassPointer) {
+ const struct Observer {
+ mutable bool called = false;
+ void setCalled() const { called = true; }
+ } observer;
+
+ const auto f = ftl::make_function<&Observer::setCalled>(&observer);
+
+ f();
+
+ EXPECT_TRUE(observer.called);
+
+ EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
+}
+
+TEST(Function, MakeWithNonCapturingLambda) {
+ auto f = ftl::make_function([](int a, int b) { return a + b; });
+ EXPECT_EQ(f(1, 2), 3);
+}
+
+TEST(Function, MakeWithCapturingLambda) {
+ bool called = false;
+ auto f = ftl::make_function([&called](int a, int b) {
+ called = true;
+ return a + b;
+ });
+ EXPECT_EQ(f(1, 2), 3);
+ EXPECT_TRUE(called);
+}
+
+TEST(Function, MakeWithCapturingMutableLambda) {
+ bool called = false;
+ auto f = ftl::make_function([&called](int a, int b) mutable {
+ called = true;
+ return a + b;
+ });
+ EXPECT_EQ(f(1, 2), 3);
+ EXPECT_TRUE(called);
+}
+
+TEST(Function, MakeWithThreePointerCapturingLambda) {
+ bool my_bool = false;
+ int my_int = 0;
+ float my_float = 0.f;
+
+ auto f = ftl::make_function(
+ [ptr_bool = &my_bool, ptr_int = &my_int, ptr_float = &my_float](int a, int b) mutable {
+ *ptr_bool = true;
+ *ptr_int = 1;
+ *ptr_float = 1.f;
+
+ return a + b;
+ });
+
+ EXPECT_EQ(f(1, 2), 3);
+
+ EXPECT_TRUE(my_bool);
+ EXPECT_EQ(my_int, 1);
+ EXPECT_EQ(my_float, 1.f);
+}
+
+TEST(Function, MakeWithFreeFunction) {
+ auto f = ftl::make_function<&std::make_unique<int, int>>();
+ std::unique_ptr<int> unique_int = f(1);
+ ASSERT_TRUE(unique_int);
+ EXPECT_EQ(*unique_int, 1);
+}
+
+TEST(Function, CopyToLarger) {
+ int counter = 0;
+ ftl::Function<void()> a{[ptr_counter = &counter] { (*ptr_counter)++; }};
+ ftl::Function<void(), 1> b = a;
+ ftl::Function<void(), 2> c = a;
+
+ EXPECT_EQ(counter, 0);
+ a();
+ EXPECT_EQ(counter, 1);
+ b();
+ EXPECT_EQ(counter, 2);
+ c();
+ EXPECT_EQ(counter, 3);
+
+ b = [ptr_counter = &counter] { (*ptr_counter) += 2; };
+ c = [ptr_counter = &counter] { (*ptr_counter) += 3; };
+
+ b();
+ EXPECT_EQ(counter, 5);
+ c();
+ EXPECT_EQ(counter, 8);
+}
+
+} // namespace android::test
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..eb4d3df 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 {
@@ -415,7 +426,6 @@
"libsync",
"libui",
"libutils",
- "libvndksupport",
],
static_libs: [
@@ -460,6 +470,7 @@
static_libs: [
"libgtest",
"libgmock",
+ "libguiflags",
],
srcs: [
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index dd0a028..f317a2e 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>
@@ -104,12 +104,11 @@
}
}
-void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
- const sp<Fence>& glDoneFence,
- const sp<Fence>& presentFence,
- const sp<Fence>& prevReleaseFence,
- CompositorTiming compositorTiming,
- nsecs_t latchTime, nsecs_t dequeueReadyTime) {
+void BLASTBufferItemConsumer::updateFrameTimestamps(
+ uint64_t frameNumber, uint64_t previousFrameNumber, nsecs_t refreshStartTime,
+ const sp<Fence>& glDoneFence, const sp<Fence>& presentFence,
+ const sp<Fence>& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime,
+ nsecs_t dequeueReadyTime) {
Mutex::Autolock lock(mMutex);
// if the producer is not connected, don't bother updating,
@@ -120,7 +119,15 @@
std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(prevReleaseFence);
mFrameEventHistory.addLatch(frameNumber, latchTime);
- mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+ if (flags::frametimestamps_previousrelease()) {
+ if (previousFrameNumber > 0) {
+ mFrameEventHistory.addRelease(previousFrameNumber, dequeueReadyTime,
+ std::move(releaseFenceTime));
+ }
+ } else {
+ mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+ }
+
mFrameEventHistory.addPreComposition(frameNumber, refreshStartTime);
mFrameEventHistory.addPostComposition(frameNumber, glDoneFenceTime, presentFenceTime,
compositorTiming);
@@ -144,7 +151,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();
@@ -366,6 +373,7 @@
if (stat.latchTime > 0) {
mBufferItemConsumer
->updateFrameTimestamps(stat.frameEventStats.frameNumber,
+ stat.frameEventStats.previousFrameNumber,
stat.frameEventStats.refreshStartTime,
stat.frameEventStats.gpuCompositionDoneFence,
stat.presentFence, stat.previousReleaseFence,
diff --git a/libs/gui/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..11f5174 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -36,9 +36,8 @@
#include <gui/TraceUtils.h>
#include <private/gui/BufferQueueThreadState.h>
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
#include <binder/PermissionCache.h>
-#include <vndksupport/linker.h>
#endif
#include <system/window.h>
@@ -318,35 +317,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;
}
@@ -802,18 +810,14 @@
const uid_t uid = BufferQueueThreadState::getCallingUid();
#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
// permission check can't be done for vendors as vendors have no access to
- // the PermissionController. We need to do a runtime check as well, since
- // the system variant of libgui can be loaded in a vendor process. For eg:
- // if a HAL uses an llndk library that depends on libgui (libmediandk etc).
- if (!android_is_in_vendor_process()) {
- const pid_t pid = BufferQueueThreadState::getCallingPid();
- if ((uid != shellUid) &&
- !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
- outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
- "from pid=%d, uid=%d\n",
- pid, uid);
- denied = true;
- }
+ // the PermissionController.
+ const pid_t pid = BufferQueueThreadState::getCallingPid();
+ if ((uid != shellUid) &&
+ !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
+ outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+ "from pid=%d, uid=%d\n",
+ pid, uid);
+ denied = true;
}
#else
if (uid != shellUid) {
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/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 93df124..7d37fd3 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -344,6 +344,13 @@
handleRefreshRateUpdates();
}
+void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) {
+ ALOGV("choreographer %p ~ received hdcp levels change event (displayId=%s, connectedLevel=%d, "
+ "maxLevel=%d), ignoring.",
+ this, to_string(displayId).c_str(), connectedLevel, maxLevel);
+}
+
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
case MSG_SCHEDULE_CALLBACKS:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 5dd058c..f3de96d 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -195,6 +195,11 @@
dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId,
std::move(mFrameRateOverrides));
break;
+ case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ dispatchHdcpLevelsChanged(ev.header.displayId,
+ ev.hdcpLevelsChange.connectedLevel,
+ ev.hdcpLevelsChange.maxLevel);
+ break;
default:
ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
break;
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/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 29d64af..f5d19aa 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -25,6 +25,10 @@
#include <gui/LayerState.h>
#include <private/gui/ParcelUtils.h>
+#include <com_android_graphics_libgui_flags.h>
+
+using namespace com::android::graphics::libgui;
+
namespace android {
namespace { // Anonymous
@@ -49,6 +53,11 @@
status_t err = output->writeUint64(frameNumber);
if (err != NO_ERROR) return err;
+ if (flags::frametimestamps_previousrelease()) {
+ err = output->writeUint64(previousFrameNumber);
+ if (err != NO_ERROR) return err;
+ }
+
if (gpuCompositionDoneFence) {
err = output->writeBool(true);
if (err != NO_ERROR) return err;
@@ -79,6 +88,11 @@
status_t err = input->readUint64(&frameNumber);
if (err != NO_ERROR) return err;
+ if (flags::frametimestamps_previousrelease()) {
+ err = input->readUint64(&previousFrameNumber);
+ if (err != NO_ERROR) return err;
+ }
+
bool hasFence = false;
err = input->readBool(&hasFence);
if (err != NO_ERROR) return err;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index fd8fc8d..38fab9c 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,7 +86,7 @@
defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
frameRateCategory(ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT),
frameRateCategorySmoothSwitchOnly(false),
- frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF),
+ frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE),
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
isTrustedOverlay(false),
@@ -930,7 +930,6 @@
SAFE_PARCEL(output->writeStrongBinder, displayToken);
SAFE_PARCEL(output->writeUint32, width);
SAFE_PARCEL(output->writeUint32, height);
- SAFE_PARCEL(output->writeBool, useIdentityTransform);
return NO_ERROR;
}
@@ -940,7 +939,6 @@
SAFE_PARCEL(input->readStrongBinder, &displayToken);
SAFE_PARCEL(input->readUint32, &width);
SAFE_PARCEL(input->readUint32, &height);
- SAFE_PARCEL(input->readBool, &useIdentityTransform);
return NO_ERROR;
}
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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a351811..8d18551 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2782,9 +2782,16 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::setOverrideFrameRate(uid_t uid, float frameRate) {
+status_t SurfaceComposerClient::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()->setOverrideFrameRate(uid, frameRate);
+ ComposerServiceAIDL::getComposerService()->setGameModeFrameRateOverride(uid, frameRate);
+ return statusTFromBinderStatus(status);
+}
+
+status_t SurfaceComposerClient::setGameDefaultFrameRateOverride(uid_t uid, float frameRate) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->setGameDefaultFrameRateOverride(uid,
+ frameRate);
return statusTFromBinderStatus(status);
}
@@ -3110,7 +3117,6 @@
->removeWindowInfosListener(windowInfosListener,
ComposerServiceAIDL::getComposerService());
}
-
// ----------------------------------------------------------------------------
status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
@@ -3122,12 +3128,12 @@
return statusTFromBinderStatus(status);
}
-status_t ScreenshotClient::captureDisplay(DisplayId displayId,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId, const gui::CaptureArgs& captureArgs,
const sp<IScreenCaptureListener>& captureListener) {
sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService());
if (s == nullptr) return NO_INIT;
- binder::Status status = s->captureDisplayById(displayId.value, captureListener);
+ binder::Status status = s->captureDisplayById(displayId.value, captureArgs, captureListener);
return statusTFromBinderStatus(status);
}
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 2eb6bd6..ba1d196 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -26,6 +26,41 @@
namespace android::gui {
+namespace {
+
+std::ostream& operator<<(std::ostream& out, const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ out << "<null>";
+ } else {
+ out << binder.get();
+ }
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Region& region) {
+ if (region.isEmpty()) {
+ out << "<empty>";
+ return out;
+ }
+
+ bool first = true;
+ Region::const_iterator cur = region.begin();
+ Region::const_iterator const tail = region.end();
+ while (cur != tail) {
+ if (first) {
+ first = false;
+ } else {
+ out << "|";
+ }
+ out << "[" << cur->left << "," << cur->top << "][" << cur->right << "," << cur->bottom
+ << "]";
+ cur++;
+ }
+ return out;
+}
+
+} // namespace
+
void WindowInfo::setInputConfig(ftl::Flags<InputConfig> config, bool value) {
if (value) {
inputConfig |= config;
@@ -66,8 +101,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 +137,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 +188,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) ?:
@@ -217,4 +257,24 @@
void WindowInfoHandle::updateFrom(sp<WindowInfoHandle> handle) {
mInfo = handle->mInfo;
}
+
+std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) {
+ const WindowInfo& info = *window.getInfo();
+ std::string transform;
+ info.transform.dump(transform, "transform", " ");
+ out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId
+ << ", inputConfig=" << info.inputConfig.string() << ", alpha=" << info.alpha << ", frame=["
+ << info.frame.left << "," << info.frame.top << "][" << info.frame.right << ","
+ << info.frame.bottom << "], globalScale=" << info.globalScaleFactor
+ << ", applicationInfo.name=" << info.applicationInfo.name
+ << ", applicationInfo.token=" << info.applicationInfo.token
+ << ", touchableRegion=" << info.touchableRegion << ", ownerPid=" << info.ownerPid.toString()
+ << ", ownerUid=" << info.ownerUid.toString() << ", dispatchingTimeout="
+ << std::chrono::duration_cast<std::chrono::milliseconds>(info.dispatchingTimeout).count()
+ << "ms, token=" << info.token.get()
+ << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n"
+ << transform;
+ return out;
+}
+
} // namespace android::gui
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/aidl/android/gui/CaptureArgs.aidl
similarity index 77%
rename from libs/gui/include/gui/Flags.h
rename to libs/gui/aidl/android/gui/CaptureArgs.aidl
index a2cff56..920d949 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#pragma once
+package android.gui;
-// 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
+parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h";
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index a7cf5dd..e3122bc 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -16,6 +16,7 @@
package android.gui;
+import android.gui.CaptureArgs;
import android.gui.Color;
import android.gui.CompositionPreference;
import android.gui.ContentSamplingAttributes;
@@ -238,7 +239,8 @@
* Capture the specified screen. This requires the READ_FRAME_BUFFER
* permission.
*/
- oneway void captureDisplayById(long displayId, IScreenCaptureListener listener);
+ oneway void captureDisplayById(long displayId, in CaptureArgs args,
+ IScreenCaptureListener listener);
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
@@ -475,10 +477,21 @@
/**
* Set the override frame rate for a specified uid by GameManagerService.
+ * This override is controlled by game mode interventions.
+ * Passing the frame rate and uid to SurfaceFlinger to update the override mapping
+ * in the LayerHistory.
+ */
+ void setGameModeFrameRateOverride(int uid, float frameRate);
+
+ /**
+ * Set the override frame rate for a specified uid by GameManagerService.
+ * This override is controlled by game default frame rate sysprop:
+ * "ro.surface_flinger.game_default_frame_rate_override" holding the override value,
+ * "persisit.graphics.game_default_frame_rate.enabled" to determine if it's enabled.
* Passing the frame rate and uid to SurfaceFlinger to update the override mapping
* in the scheduler.
*/
- void setOverrideFrameRate(int uid, float frameRate);
+ void setGameDefaultFrameRateOverride(int uid, float frameRate);
oneway void updateSmallAreaDetection(in int[] appIds, in float[] thresholds);
@@ -514,6 +527,13 @@
void scheduleCommit();
/**
+ * Force all window composition to the GPU (i.e. disable Hardware Overlays).
+ * This can help check if there is a bug in HW Composer.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void forceClientComposition(boolean enabled);
+
+ /**
* Gets priority of the RenderEngine in SurfaceFlinger.
*/
int getGpuContextPriority();
diff --git a/libs/gui/android/gui/TouchOcclusionMode.aidl b/libs/gui/android/gui/TouchOcclusionMode.aidl
index d91d052..ed72105 100644
--- a/libs/gui/android/gui/TouchOcclusionMode.aidl
+++ b/libs/gui/android/gui/TouchOcclusionMode.aidl
@@ -43,5 +43,6 @@
* The window won't count for touch occlusion rules if the touch passes
* through it.
*/
- ALLOW
+ ALLOW,
+ ftl_last=ALLOW,
}
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
index 17f4c63..2e270b7 100644
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
@@ -141,8 +141,8 @@
CompositorTiming compTiming;
sp<Fence> previousFence = new Fence(memfd_create("pfd", MFD_ALLOW_SEALING));
sp<Fence> gpuFence = new Fence(memfd_create("gfd", MFD_ALLOW_SEALING));
- FrameEventHistoryStats frameStats(frameNumber, gpuFence, compTiming,
- mFdp.ConsumeIntegral<int64_t>(),
+ FrameEventHistoryStats frameStats(frameNumber, mFdp.ConsumeIntegral<uint64_t>(), gpuFence,
+ compTiming, mFdp.ConsumeIntegral<int64_t>(),
mFdp.ConsumeIntegral<int64_t>());
std::vector<SurfaceControlStats> stats;
sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 3142103..c952ba2 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -100,8 +100,8 @@
MOCK_METHOD(binder::Status, setGameContentType, (const sp<IBinder>&, bool), (override));
MOCK_METHOD(binder::Status, captureDisplay,
(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
- MOCK_METHOD(binder::Status, captureDisplayById, (int64_t, const sp<IScreenCaptureListener>&),
- (override));
+ MOCK_METHOD(binder::Status, captureDisplayById,
+ (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override));
MOCK_METHOD(binder::Status, captureLayers,
(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override));
MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override));
@@ -149,11 +149,13 @@
(const gui::Color&, const gui::Color&, float, float, float), (override));
MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
(const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
- MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, setGameModeFrameRateOverride, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, setGameDefaultFrameRateOverride, (int32_t, float), (override));
MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override));
MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
MOCK_METHOD(binder::Status, scheduleCommit, (), (override));
+ MOCK_METHOD(binder::Status, forceClientComposition, (bool), (override));
MOCK_METHOD(binder::Status, updateSmallAreaDetection,
(const std::vector<int32_t>&, const std::vector<float>&), (override));
MOCK_METHOD(binder::Status, setSmallAreaDetectionThreshold, (int32_t, float), (override));
@@ -205,6 +207,7 @@
MOCK_METHOD2(dispatchNullEvent, void(nsecs_t, PhysicalDisplayId));
MOCK_METHOD3(dispatchFrameRateOverrides,
void(nsecs_t, PhysicalDisplayId, std::vector<FrameRateOverride>));
+ MOCK_METHOD3(dispatchHdcpLevelsChanged, void(PhysicalDisplayId, int32_t, int32_t));
};
} // namespace android
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 02d7c4d..0e1a505 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;
@@ -48,8 +50,8 @@
void onDisconnect() override EXCLUDES(mMutex);
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex);
- void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
- const sp<Fence>& gpuCompositionDoneFence,
+ void updateFrameTimestamps(uint64_t frameNumber, uint64_t previousFrameNumber,
+ nsecs_t refreshStartTime, const sp<Fence>& gpuCompositionDoneFence,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
CompositorTiming compositorTiming, nsecs_t latchTime,
nsecs_t dequeueReadyTime) EXCLUDES(mMutex);
@@ -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/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 9fef512..55a7aa7 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -116,6 +116,8 @@
void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
+ void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) override;
void scheduleCallbacks();
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
index 2676e0a..e29ce41 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -76,7 +76,6 @@
sp<IBinder> displayToken;
uint32_t width{0};
uint32_t height{0};
- bool useIdentityTransform{false};
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index fe2dd20..82cd50c 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -65,6 +65,9 @@
virtual void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) = 0;
+ virtual void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) = 0;
+
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 79582ce..8c1103b 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -58,7 +58,6 @@
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
-
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -66,6 +65,7 @@
DISPLAY_EVENT_NULL = fourcc('n', 'u', 'l', 'l'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE = fourcc('r', 'a', 't', 'e'),
DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH = fourcc('f', 'l', 's', 'h'),
+ DISPLAY_EVENT_HDCP_LEVELS_CHANGE = fourcc('h', 'd', 'c', 'p'),
};
struct Event {
@@ -101,12 +101,22 @@
float frameRateHz __attribute__((aligned(8)));
};
+ /*
+ * The values are defined in aidl:
+ * hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl
+ */
+ struct HdcpLevelsChange {
+ int32_t connectedLevel;
+ int32_t maxLevel;
+ };
+
Header header;
union {
VSync vsync;
Hotplug hotplug;
ModeChange modeChange;
FrameRateOverride frameRateOverride;
+ HdcpLevelsChange hdcpLevelsChange;
};
};
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/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 364a57b..bc97cd0 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -95,15 +95,18 @@
status_t readFromParcel(const Parcel* input) override;
FrameEventHistoryStats() = default;
- FrameEventHistoryStats(uint64_t fn, const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
+ FrameEventHistoryStats(uint64_t frameNumber, uint64_t previousFrameNumber,
+ const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
nsecs_t refreshTime, nsecs_t dequeueReadyTime)
- : frameNumber(fn),
+ : frameNumber(frameNumber),
+ previousFrameNumber(previousFrameNumber),
gpuCompositionDoneFence(gpuCompFence),
compositorTiming(compTiming),
refreshStartTime(refreshTime),
dequeueReadyTime(dequeueReadyTime) {}
uint64_t frameNumber;
+ uint64_t previousFrameNumber;
sp<Fence> gpuCompositionDoneFence;
CompositorTiming compositorTiming;
nsecs_t refreshStartTime;
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index bf354e7..1fc80c3 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -18,7 +18,7 @@
namespace android {
-// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry.
+// Jank type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry.
enum JankType {
// No Jank
None = 0x0,
@@ -50,4 +50,16 @@
Dropped = 0x200,
};
+// Jank severity type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry.
+enum class JankSeverityType {
+ // Unknown: not enough information to classify the severity of a jank
+ Unknown = 0,
+ // None: no jank
+ None = 1,
+ // Partial: jank caused by missing the deadline by less than the app's frame interval
+ Partial = 2,
+ // Full: jank caused by missing the deadline by more than the app's frame interval
+ Full = 3,
+};
+
} // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 54c3aa7..14e3dd5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -201,7 +201,13 @@
// Sets the frame rate of a particular app (uid). This is currently called
// by GameManager.
- static status_t setOverrideFrameRate(uid_t uid, float frameRate);
+ static status_t setGameModeFrameRateOverride(uid_t uid, float frameRate);
+
+ // Sets the frame rate of a particular app (uid). This is currently called
+ // by GameManager and controlled by two sysprops:
+ // "ro.surface_flinger.game_default_frame_rate_override" holding the override value,
+ // "persisit.graphics.game_default_frame_rate.enabled" to determine if it's enabled.
+ static status_t setGameDefaultFrameRateOverride(uid_t uid, float frameRate);
// Update the small area detection whole appId-threshold mappings by same size appId and
// threshold vector.
@@ -841,8 +847,14 @@
class ScreenshotClient {
public:
static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
- static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ static status_t captureDisplay(DisplayId, const gui::CaptureArgs&,
+ const sp<IScreenCaptureListener>&);
static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
+
+ [[deprecated]] static status_t captureDisplay(DisplayId id,
+ const sp<IScreenCaptureListener>& listener) {
+ return captureDisplay(id, gui::CaptureArgs(), listener);
+ }
};
// ---------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index bd2eb74..b72b71a 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>
@@ -175,6 +176,8 @@
static_cast<uint32_t>(os::InputConfig::INTERCEPTS_STYLUS),
CLONE =
static_cast<uint32_t>(os::InputConfig::CLONE),
+ GLOBAL_STYLUS_BLOCKS_TOUCH =
+ static_cast<uint32_t>(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH),
// clang-format on
};
@@ -196,6 +199,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
@@ -311,4 +317,7 @@
WindowInfo mInfo;
};
+
+std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window);
+
} // namespace android::gui
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index f7dcbc6..b7aba2b 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -24,9 +24,9 @@
#include <binder/IBinder.h>
#include <binder/Parcelable.h>
-namespace android {
+#include <gui/IGraphicBufferProducer.h>
-class IGraphicBufferProducer;
+namespace android {
namespace view {
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index a16be78..b081030 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -8,3 +8,10 @@
is_fixed_read_only: true
}
+flag {
+ name: "frametimestamps_previousrelease"
+ namespace: "core_graphics"
+ description: "Controls a fence fixup for timestamp apis"
+ bug: "310927247"
+ is_fixed_read_only: true
+}
diff --git a/libs/gui/tests/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/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9893c71..ea7078d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -42,9 +42,12 @@
#include <gtest/gtest.h>
+#include <com_android_graphics_libgui_flags.h>
+
using namespace std::chrono_literals;
namespace android {
+using namespace com::android::graphics::libgui;
using Transaction = SurfaceComposerClient::Transaction;
using android::hardware::graphics::common::V1_2::BufferUsage;
@@ -1581,6 +1584,9 @@
nsecs_t postedTimeB = 0;
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
+
+ adapter.waitForCallback(2);
+
events = history.getFrame(1);
ASSERT_NE(nullptr, events);
@@ -1590,7 +1596,9 @@
ASSERT_GE(events->postedTime, postedTimeA);
ASSERT_GE(events->latchTime, postedTimeA);
- ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+ if (flags::frametimestamps_previousrelease()) {
+ ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING);
+ }
ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
ASSERT_NE(nullptr, events->displayPresentFence);
ASSERT_NE(nullptr, events->releaseFence);
@@ -1602,6 +1610,50 @@
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
+ // Now do the same as above with a third buffer, so that timings related to
+ // buffer releases make it back to the first frame.
+ nsecs_t requestedPresentTimeC = 0;
+ nsecs_t postedTimeC = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ adapter.waitForCallback(3);
+
+ // Check the first frame...
+ events = history.getFrame(1);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+ ASSERT_GE(events->latchTime, postedTimeA);
+ // Now dequeueReadyTime is valid, because the release timings finally
+ // propaged to queueBuffer()
+ ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // ...and the second
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+ ASSERT_GE(events->latchTime, postedTimeB);
+ if (flags::frametimestamps_previousrelease()) {
+ ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING);
+ }
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // ...and finally the third!
+ events = history.getFrame(3);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(3, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeC, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeC);
+
// wait for any callbacks that have not been received
adapter.waitForCallbacks();
}
@@ -1660,6 +1712,8 @@
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
+ adapter.waitForCallback(3);
+
// frame number, requestedPresentTime, and postTime should not have changed
ASSERT_EQ(1, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
@@ -1679,6 +1733,42 @@
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
ASSERT_GE(events->latchTime, postedTimeB);
+
+ if (flags::frametimestamps_previousrelease()) {
+ ASSERT_EQ(events->dequeueReadyTime, FrameEvents::TIMESTAMP_PENDING);
+ }
+ ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+ ASSERT_NE(nullptr, events->displayPresentFence);
+ ASSERT_NE(nullptr, events->releaseFence);
+
+ // Queue another buffer to check for timestamps that came late
+ nsecs_t requestedPresentTimeD = 0;
+ nsecs_t postedTimeD = 0;
+ setUpAndQueueBuffer(igbProducer, &requestedPresentTimeD, &postedTimeD, &qbOutput, true);
+ history.applyDelta(qbOutput.frameTimestamps);
+
+ adapter.waitForCallback(4);
+
+ // frame number, requestedPresentTime, and postTime should not have changed
+ events = history.getFrame(1);
+ ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeA);
+
+ // a valid latchtime and pre and post composition info should not be set for the dropped frame
+ ASSERT_FALSE(events->hasLatchInfo());
+ ASSERT_FALSE(events->hasDequeueReadyInfo());
+ ASSERT_FALSE(events->hasGpuCompositionDoneInfo());
+ ASSERT_FALSE(events->hasDisplayPresentInfo());
+ ASSERT_FALSE(events->hasReleaseInfo());
+
+ // we should also have gotten values for the presented frame
+ events = history.getFrame(2);
+ ASSERT_NE(nullptr, events);
+ ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+ ASSERT_GE(events->postedTime, postedTimeB);
+ ASSERT_GE(events->latchTime, postedTimeB);
ASSERT_GE(events->dequeueReadyTime, events->latchTime);
ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
ASSERT_NE(nullptr, events->displayPresentFence);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 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..c6ea317 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;
@@ -851,7 +786,8 @@
return binder::Status::ok();
}
- binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override {
+ binder::Status captureDisplayById(int64_t, const gui::CaptureArgs&,
+ const sp<IScreenCaptureListener>&) override {
return binder::Status::ok();
}
@@ -985,7 +921,11 @@
return binder::Status::ok();
}
- binder::Status setOverrideFrameRate(int32_t /*uid*/, float /*frameRate*/) override {
+ binder::Status setGameModeFrameRateOverride(int32_t /*uid*/, float /*frameRate*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status setGameDefaultFrameRateOverride(int32_t /*uid*/, float /*frameRate*/) override {
return binder::Status::ok();
}
@@ -999,6 +939,10 @@
binder::Status scheduleCommit() override { return binder::Status::ok(); }
+ binder::Status forceClientComposition(bool /*enabled*/) override {
+ return binder::Status::ok();
+ }
+
binder::Status updateSmallAreaDetection(const std::vector<int32_t>& /*appIds*/,
const std::vector<float>& /*thresholds*/) {
return binder::Status::ok();
@@ -1341,7 +1285,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/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 198908d..7c15e7c 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -20,7 +20,6 @@
#include <android/binder_parcel.h>
#include <android/native_window.h>
#include <binder/Parcel.h>
-#include <gui/IGraphicBufferProducer.h>
#include <gui/Surface.h>
#include <gui/view/Surface.h>
#include <system/window.h>
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/Input.cpp b/libs/input/Input.cpp
index bd5b67b..8eaff00 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -379,6 +379,11 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const PointerProperties& properties) {
+ out << "Pointer(id=" << properties.id << ", " << ftl::enum_string(properties.toolType) << ")";
+ return out;
+}
+
// --- PointerCoords ---
float PointerCoords::getAxisValue(int32_t axis) const {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 83453af..598f949 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -447,7 +447,7 @@
msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(getFd().get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
@@ -479,7 +479,7 @@
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
- nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
+ nRead = ::recv(getFd().get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
@@ -568,7 +568,7 @@
}
base::unique_fd InputChannel::dupFd() const {
- android::base::unique_fd newFd(::dup(getFd()));
+ base::unique_fd newFd(::dup(getFd().get()));
if (!newFd.ok()) {
ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
strerror(errno));
@@ -663,7 +663,7 @@
"action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32 " \n%s",
+ "pointerCount=%" PRIu32 "\n%s",
mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
inputEventSourceToString(source).c_str(), displayId,
MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags,
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/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 412931b..c4e3ff6 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -60,9 +60,11 @@
// --- MotionPredictor ---
MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
- std::function<bool()> checkMotionPredictionEnabled)
+ std::function<bool()> checkMotionPredictionEnabled,
+ ReportAtomFunction reportAtomFunction)
: mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos),
- mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {}
+ mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)),
+ mReportAtomFunction(reportAtomFunction) {}
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
@@ -90,6 +92,13 @@
mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
}
+ // Pass input event to the MetricsManager.
+ if (!mMetricsManager) {
+ mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength(),
+ mReportAtomFunction);
+ }
+ mMetricsManager->onRecord(event);
+
const int32_t action = event.getActionMasked();
if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
@@ -135,12 +144,6 @@
}
mLastEvent->copyFrom(&event, /*keepHistory=*/false);
- // Pass input event to the MetricsManager.
- if (!mMetricsManager) {
- mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength());
- }
- mMetricsManager->onRecord(event);
-
return {};
}
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
index 67b1032..0412d08 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -46,13 +46,36 @@
} // namespace
-MotionPredictorMetricsManager::MotionPredictorMetricsManager(nsecs_t predictionInterval,
- size_t maxNumPredictions)
+void MotionPredictorMetricsManager::defaultReportAtomFunction(
+ const MotionPredictorMetricsManager::AtomFields& atomFields) {
+ // Call stats_write logging function only on Android targets (not supported on host).
+#ifdef __ANDROID__
+ android::stats::libinput::
+ stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
+ /*stylus_vendor_id=*/0,
+ /*stylus_product_id=*/0,
+ atomFields.deltaTimeBucketMilliseconds,
+ atomFields.alongTrajectoryErrorMeanMillipixels,
+ atomFields.alongTrajectoryErrorStdMillipixels,
+ atomFields.offTrajectoryRmseMillipixels,
+ atomFields.pressureRmseMilliunits,
+ atomFields.highVelocityAlongTrajectoryRmse,
+ atomFields.highVelocityOffTrajectoryRmse,
+ atomFields.scaleInvariantAlongTrajectoryRmse,
+ atomFields.scaleInvariantOffTrajectoryRmse);
+#endif
+}
+
+MotionPredictorMetricsManager::MotionPredictorMetricsManager(
+ nsecs_t predictionInterval,
+ size_t maxNumPredictions,
+ ReportAtomFunction reportAtomFunction)
: mPredictionInterval(predictionInterval),
mMaxNumPredictions(maxNumPredictions),
mRecentGroundTruthPoints(maxNumPredictions + 1),
mAggregatedMetrics(maxNumPredictions),
- mAtomFields(maxNumPredictions) {}
+ mAtomFields(maxNumPredictions),
+ mReportAtomFunction(reportAtomFunction ? reportAtomFunction : defaultReportAtomFunction) {}
void MotionPredictorMetricsManager::onRecord(const MotionEvent& inputEvent) {
// Convert MotionEvent to GroundTruthPoint.
@@ -81,8 +104,8 @@
if (mRecentGroundTruthPoints.size() >= 2) {
computeAtomFields();
reportMetrics();
- break;
}
+ break;
}
}
}
@@ -345,28 +368,10 @@
}
void MotionPredictorMetricsManager::reportMetrics() {
- // Report one atom for each time bucket.
+ LOG_ALWAYS_FATAL_IF(!mReportAtomFunction);
+ // Report one atom for each prediction time bucket.
for (size_t i = 0; i < mAtomFields.size(); ++i) {
- // Call stats_write logging function only on Android targets (not supported on host).
-#ifdef __ANDROID__
- android::stats::libinput::
- stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
- /*stylus_vendor_id=*/0,
- /*stylus_product_id=*/0, mAtomFields[i].deltaTimeBucketMilliseconds,
- mAtomFields[i].alongTrajectoryErrorMeanMillipixels,
- mAtomFields[i].alongTrajectoryErrorStdMillipixels,
- mAtomFields[i].offTrajectoryRmseMillipixels,
- mAtomFields[i].pressureRmseMilliunits,
- mAtomFields[i].highVelocityAlongTrajectoryRmse,
- mAtomFields[i].highVelocityOffTrajectoryRmse,
- mAtomFields[i].scaleInvariantAlongTrajectoryRmse,
- mAtomFields[i].scaleInvariantOffTrajectoryRmse);
-#endif
- }
-
- // Set mock atom fields, if available.
- if (mMockLoggedAtomFields != nullptr) {
- *mMockLoggedAtomFields = mAtomFields;
+ mReportAtomFunction(mAtomFields[i]);
}
}
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/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index 9a459b1..db7031a 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -193,6 +193,7 @@
{AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
{AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
{AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
+ {AKEYCODE_LANGUAGE_SWITCH, KEY_LANGUAGE},
};
VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
VirtualKeyboard::~VirtualKeyboard() {}
diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl
index 4e644ff..5d39155 100644
--- a/libs/input/android/os/InputConfig.aidl
+++ b/libs/input/android/os/InputConfig.aidl
@@ -150,4 +150,11 @@
* likely a duplicate window with the same client token, but different bounds.
*/
CLONE = 1 << 16,
+
+ /**
+ * If the stylus is currently down *anywhere* on the screen, new touches will not be delivered
+ * to the window with this flag. This helps prevent unexpected clicks on some system windows,
+ * like StatusBar and TaskBar.
+ */
+ GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17,
}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 6302ff5..11f6994 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -41,3 +41,59 @@
description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
bug: "299977100"
}
+
+flag {
+ name: "report_palms_to_gestures_library"
+ namespace: "input"
+ description: "Report touches marked as palm by firmware to gestures library"
+ bug: "302505955"
+}
+
+flag {
+ name: "enable_touchpad_typing_palm_rejection"
+ namespace: "input"
+ description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard"
+ bug: "301055381"
+}
+
+flag {
+ name: "enable_v2_touchpad_typing_palm_rejection"
+ namespace: "input"
+ description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click."
+ 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"
+}
+
+flag {
+ name: "enable_input_filter_rust_impl"
+ namespace: "input"
+ description: "Enable input filter rust implementation"
+ bug: "294546335"
+}
+
+flag {
+ name: "override_key_behavior_permission_apis"
+ namespace: "input"
+ description: "enable override key behavior permission APIs"
+ bug: "309018874"
+}
+
+flag {
+ name: "remove_pointer_event_tracking_in_wm"
+ namespace: "input"
+ description: "Remove pointer event tracking in WM after the Pointer Icon Refactor"
+ bug: "315321016"
+}
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index bbc6d98..867af79 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -118,18 +118,14 @@
match action.into() {
MotionAction::Down => {
- 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) {
+ 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, it
+ self.name, device_id, self.touching_pointer_ids_by_device
));
}
- it.insert(pointer_id);
+ let it = self.touching_pointer_ids_by_device.entry(device_id).or_default();
+ it.insert(pointer_properties[0].id);
}
MotionAction::PointerDown { action_index } => {
if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
@@ -225,19 +221,13 @@
self.name, device_id, self.hovering_pointer_ids_by_device
));
}
- let it = self
- .hovering_pointer_ids_by_device
- .entry(device_id)
- .or_insert_with(HashSet::new);
+ let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
it.insert(pointer_properties[0].id);
}
MotionAction::HoverMove => {
// For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
// If there was no prior HOVER_ENTER, just start a new hovering pointer.
- let it = self
- .hovering_pointer_ids_by_device
- .entry(device_id)
- .or_insert_with(HashSet::new);
+ let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
it.insert(pointer_properties[0].id);
}
MotionAction::HoverExit => {
@@ -282,6 +272,10 @@
return false;
};
+ if pointers.len() != pointer_properties.len() {
+ return false;
+ }
+
for pointer_property in pointer_properties.iter() {
let pointer_id = pointer_property.id;
if !pointers.contains(&pointer_id) {
@@ -353,6 +347,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 }]);
@@ -552,4 +596,43 @@
)
.is_ok());
}
+
+ // Send a MOVE event with incorrect number of pointers (one of the pointers is missing).
+ #[test]
+ fn move_with_wrong_number_of_pointers() {
+ 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());
+ // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
}
diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
index b420a5a..31cc145 100644
--- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -39,6 +39,7 @@
using GroundTruthPoint = MotionPredictorMetricsManager::GroundTruthPoint;
using PredictionPoint = MotionPredictorMetricsManager::PredictionPoint;
using AtomFields = MotionPredictorMetricsManager::AtomFields;
+using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
inline constexpr int NANOS_PER_MILLIS = 1'000'000;
@@ -664,9 +665,16 @@
// --- MotionPredictorMetricsManager tests. ---
-// Helper function that instantiates a MetricsManager with the given mock logged AtomFields. Takes
-// vectors of ground truth and prediction points of the same length, and passes these points to the
-// MetricsManager. The format of these vectors is expected to be:
+// Creates a mock atom reporting function that appends the reported atom to the given vector.
+ReportAtomFunction createMockReportAtomFunction(std::vector<AtomFields>& reportedAtomFields) {
+ return [&reportedAtomFields](const AtomFields& atomFields) -> void {
+ reportedAtomFields.push_back(atomFields);
+ };
+}
+
+// Helper function that instantiates a MetricsManager that reports metrics to outReportedAtomFields.
+// Takes vectors of ground truth and prediction points of the same length, and passes these points
+// to the MetricsManager. The format of these vectors is expected to be:
// • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements.
// • predictionPoints: the first index points to a vector of predictions corresponding to the
// source ground truth point with the same index.
@@ -678,15 +686,16 @@
// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and
// predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2.
//
-// The passed-in outAtomFields will contain the logged AtomFields when the function returns.
+// When the function returns, outReportedAtomFields will contain the reported AtomFields.
//
// This function returns void so that it can use test assertions.
void runMetricsManager(const std::vector<GroundTruthPoint>& groundTruthPoints,
const std::vector<std::vector<PredictionPoint>>& predictionPoints,
- std::vector<AtomFields>& outAtomFields) {
+ std::vector<AtomFields>& outReportedAtomFields) {
MotionPredictorMetricsManager metricsManager(TEST_PREDICTION_INTERVAL_NANOS,
- TEST_MAX_NUM_PREDICTIONS);
- metricsManager.setMockLoggedAtomFields(&outAtomFields);
+ TEST_MAX_NUM_PREDICTIONS,
+ createMockReportAtomFunction(
+ outReportedAtomFields));
// Validate structure of groundTruthPoints and predictionPoints.
ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
@@ -712,18 +721,18 @@
// • Input: no prediction data.
// • Expectation: no metrics should be logged.
TEST(MotionPredictorMetricsManagerTest, NoPredictions) {
- std::vector<AtomFields> mockLoggedAtomFields;
+ std::vector<AtomFields> reportedAtomFields;
MotionPredictorMetricsManager metricsManager(TEST_PREDICTION_INTERVAL_NANOS,
- TEST_MAX_NUM_PREDICTIONS);
- metricsManager.setMockLoggedAtomFields(&mockLoggedAtomFields);
+ TEST_MAX_NUM_PREDICTIONS,
+ createMockReportAtomFunction(reportedAtomFields));
metricsManager.onRecord(makeMotionEvent(
GroundTruthPoint{{.position = Eigen::Vector2f(0, 0), .pressure = 0}, .timestamp = 0}));
metricsManager.onRecord(makeLiftMotionEvent());
- // Check that mockLoggedAtomFields is still empty (as it was initialized empty), ensuring that
+ // Check that reportedAtomFields is still empty (as it was initialized empty), ensuring that
// no metrics were logged.
- EXPECT_EQ(0u, mockLoggedAtomFields.size());
+ EXPECT_EQ(0u, reportedAtomFields.size());
}
// Perfect predictions test:
@@ -744,14 +753,14 @@
groundTruthPoint.timestamp += TEST_PREDICTION_INTERVAL_NANOS;
}
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
// Check that errors are all zero, or NO_DATA_SENTINEL for unreported metrics.
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
// General errors: reported for every time bucket.
@@ -764,7 +773,7 @@
EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityAlongTrajectoryRmse);
EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityOffTrajectoryRmse);
// Scale-invariant errors: reported only for the last time bucket.
- if (i + 1 == atomFields.size()) {
+ if (i + 1 == reportedAtomFields.size()) {
EXPECT_EQ(0, atom.scaleInvariantAlongTrajectoryRmse);
EXPECT_EQ(0, atom.scaleInvariantOffTrajectoryRmse);
} else {
@@ -801,14 +810,14 @@
computePressureRmses(groundTruthPoints, predictionPoints);
// Run test.
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
// Check logged metrics match expectations.
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
// Check time bucket delta matches expectation based on index and prediction interval.
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
@@ -845,14 +854,14 @@
computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
// Run test.
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
// Check logged metrics match expectations.
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
// Check time bucket delta matches expectation based on index and prediction interval.
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
@@ -896,14 +905,14 @@
computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
// Run test.
- std::vector<AtomFields> atomFields;
- runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
// Check logged metrics match expectations.
- ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
- for (size_t i = 0; i < atomFields.size(); ++i) {
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, reportedAtomFields.size());
+ for (size_t i = 0; i < reportedAtomFields.size(); ++i) {
SCOPED_TRACE(testing::Message() << "i = " << i);
- const AtomFields& atom = atomFields[i];
+ const AtomFields& atom = reportedAtomFields[i];
const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
@@ -926,7 +935,7 @@
// to general errors (where reported).
//
// As above, use absolute value for RMSE, since it must be non-negative.
- if (i + 2 >= atomFields.size()) {
+ if (i + 2 >= reportedAtomFields.size()) {
EXPECT_NEAR(static_cast<int>(
1000 * std::abs(generalPositionErrors[i].alongTrajectoryErrorMean)),
atom.highVelocityAlongTrajectoryRmse, 1);
@@ -946,7 +955,7 @@
// to scale-invariant errors by dividing by `strokeVelocty * TEST_MAX_NUM_PREDICTIONS`.
//
// As above, use absolute value for RMSE, since it must be non-negative.
- if (i + 1 == atomFields.size()) {
+ if (i + 1 == reportedAtomFields.size()) {
const float pathLength = strokeVelocity * TEST_MAX_NUM_PREDICTIONS;
std::vector<float> alongTrajectoryAbsoluteErrors;
std::vector<float> offTrajectoryAbsoluteErrors;
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 4ac7ae9..3343114 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -147,4 +147,35 @@
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
+using AtomFields = MotionPredictorMetricsManager::AtomFields;
+using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
+
+// Creates a mock atom reporting function that appends the reported atom to the given vector.
+// The passed-in pointer must not be nullptr.
+ReportAtomFunction createMockReportAtomFunction(std::vector<AtomFields>* reportedAtomFields) {
+ return [reportedAtomFields](const AtomFields& atomFields) -> void {
+ reportedAtomFields->push_back(atomFields);
+ };
+}
+
+TEST(MotionPredictorMetricsManagerIntegrationTest, ReportsMetrics) {
+ std::vector<AtomFields> reportedAtomFields;
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; },
+ createMockReportAtomFunction(&reportedAtomFields));
+
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 1, 0ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 2, 4ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 3, 3, 8ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 4, 4, 12ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 5, 5, 16ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 6, 6, 20ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(UP, 7, 7, 24ms, /*deviceId=*/0)).ok());
+
+ // The number of atoms reported should equal the number of prediction time buckets, which is
+ // given by the prediction model's output length. For now, this value is always 5, and we
+ // hardcode it because it's not publicly accessible from the MotionPredictor.
+ EXPECT_EQ(5u, reportedAtomFields.size());
+}
+
} // namespace android
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/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e7b2195..745b6f3 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -113,6 +113,22 @@
AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
"HAL and AHardwareBuffer pixel format don't match");
+static enum AHardwareBufferStatus filterStatus(status_t status) {
+ switch (status) {
+ case STATUS_OK:
+ return AHARDWAREBUFFER_STATUS_OK;
+ case STATUS_NO_MEMORY:
+ return AHARDWAREBUFFER_STATUS_NO_MEMORY;
+ case STATUS_BAD_VALUE:
+ return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ case STATUS_UNKNOWN_TRANSACTION:
+ case STATUS_INVALID_OPERATION:
+ return AHARDWAREBUFFER_STATUS_UNSUPPORTED;
+ default:
+ return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR;
+ }
+}
+
// ----------------------------------------------------------------------------
// Public functions
// ----------------------------------------------------------------------------
@@ -511,6 +527,24 @@
return AParcel_viewPlatformParcel(parcel)->write(*gb);
}
+ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) {
+ const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ if (!gb) return ADATASPACE_UNKNOWN;
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+ status_t status = gb->getDataspace(&dataspace);
+ if (status != OK) {
+ return ADATASPACE_UNKNOWN;
+ }
+ return static_cast<ADataSpace>(dataspace);
+}
+
+enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer,
+ ADataSpace dataspace) {
+ GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ auto& mapper = GraphicBufferMapper::get();
+ return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace)));
+}
+
// ----------------------------------------------------------------------------
// VNDK functions
// ----------------------------------------------------------------------------
@@ -552,6 +586,56 @@
return NO_ERROR;
}
+enum AHardwareBufferStatus AHardwareBuffer_allocate2(
+ const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions,
+ size_t additionalOptionsSize, AHardwareBuffer** outBuffer) {
+ (void)additionalOptions;
+ (void)additionalOptionsSize;
+ if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) {
+ return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ }
+
+ int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+ uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+
+ std::vector<GraphicBufferAllocator::AdditionalOptions> extras;
+ extras.reserve(additionalOptionsSize);
+ for (size_t i = 0; i < additionalOptionsSize; i++) {
+ extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name,
+ additionalOptions[i].value});
+ }
+
+ const auto extrasCount = extras.size();
+ auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{
+ .importBuffer = true,
+ .width = desc->width,
+ .height = desc->height,
+ .format = format,
+ .layerCount = desc->layers,
+ .usage = usage,
+ .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]",
+ .extras = std::move(extras),
+ });
+
+ status_t err = gbuffer->initCheck();
+ if (err != 0 || gbuffer->handle == nullptr) {
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
+ ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p",
+ desc->width, desc->height, desc->layers, extrasCount, strerror(-err),
+ gbuffer->handle);
+ return filterStatus(err == 0 ? UNKNOWN_ERROR : err);
+ }
+
+ *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
+
+ // Ensure the buffer doesn't get destroyed when the sp<> goes away.
+ AHardwareBuffer_acquire(*outBuffer);
+ return AHARDWAREBUFFER_STATUS_OK;
+}
+
// ----------------------------------------------------------------------------
// Helpers implementation
// ----------------------------------------------------------------------------
@@ -652,12 +736,9 @@
return ahardwarebuffer_format;
}
+// TODO: Remove, this is just to make an overly aggressive ABI checker happy
int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) {
- GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
- auto& mapper = GraphicBufferMapper::get();
- ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
- mapper.getDataspace(gb->handle, &dataspace);
- return static_cast<int32_t>(dataspace);
+ return ::AHardwareBuffer_getDataSpace(buffer);
}
uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index dd5958d..f97eed5 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -152,31 +152,56 @@
int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) {
static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
- static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
- static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
- static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
- static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
- static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
- static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
- static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
- static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
- static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
- static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
- static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
- static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
- static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
- static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
- static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
- static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
- static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
- static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK));
- static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
- static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL));
- static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
- static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_MASK));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_FULL));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 880c694..f145a2f 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -27,8 +27,8 @@
#include <stdint.h>
-struct AHardwareBuffer;
-struct AHardwareBuffer_Desc;
+#include <vndk/hardware_buffer.h>
+
struct ANativeWindowBuffer;
namespace android {
@@ -46,11 +46,6 @@
// convert HAL format to AHardwareBuffer format (note: this is a no-op)
uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
-// retrieves a dataspace from the AHardwareBuffer metadata, if the device
-// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not
-// supported.
-int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer);
-
// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index eab21fb..d4278be 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -37,7 +37,7 @@
/**
* ADataSpace.
*/
-enum ADataSpace {
+enum ADataSpace : int32_t {
/**
* Default-assumption data space, when not explicitly specified.
*
@@ -63,7 +63,7 @@
* Defines the chromaticity coordinates of the source primaries in terms of
* the CIE 1931 definition of x and y specified in ISO 11664-1.
*/
- STANDARD_MASK = 63 << 16,
+ ADATASPACE_STANDARD_MASK = 63 << 16,
/**
* Chromacity coordinates are unknown or are determined by the application.
@@ -78,7 +78,7 @@
* For all other formats standard is undefined, and implementations should use
* an appropriate standard for the data represented.
*/
- STANDARD_UNSPECIFIED = 0 << 16,
+ ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16,
/**
* <pre>
@@ -91,7 +91,7 @@
* Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT709 = 1 << 16,
+ ADATASPACE_STANDARD_BT709 = 1 << 16,
/**
* <pre>
@@ -106,7 +106,7 @@
* to minimize the color shift into RGB space that uses BT.709
* primaries.
*/
- STANDARD_BT601_625 = 2 << 16,
+ ADATASPACE_STANDARD_BT601_625 = 2 << 16,
/**
* <pre>
@@ -119,7 +119,7 @@
* Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT601_625_UNADJUSTED = 3 << 16,
+ ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16,
/**
* <pre>
@@ -134,7 +134,7 @@
* to minimize the color shift into RGB space that uses BT.709
* primaries.
*/
- STANDARD_BT601_525 = 4 << 16,
+ ADATASPACE_STANDARD_BT601_525 = 4 << 16,
/**
* <pre>
@@ -147,7 +147,7 @@
* Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
* for RGB conversion (as in SMPTE 240M).
*/
- STANDARD_BT601_525_UNADJUSTED = 5 << 16,
+ ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16,
/**
* <pre>
@@ -160,7 +160,7 @@
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT2020 = 6 << 16,
+ ADATASPACE_STANDARD_BT2020 = 6 << 16,
/**
* <pre>
@@ -173,7 +173,7 @@
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion using the linear domain.
*/
- STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
+ ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
/**
* <pre>
@@ -186,7 +186,7 @@
* Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT470M = 8 << 16,
+ ADATASPACE_STANDARD_BT470M = 8 << 16,
/**
* <pre>
@@ -199,7 +199,7 @@
* Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
* for RGB conversion.
*/
- STANDARD_FILM = 9 << 16,
+ ADATASPACE_STANDARD_FILM = 9 << 16,
/**
* SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
@@ -210,7 +210,7 @@
* red 0.680 0.320
* white (D65) 0.3127 0.3290</pre>
*/
- STANDARD_DCI_P3 = 10 << 16,
+ ADATASPACE_STANDARD_DCI_P3 = 10 << 16,
/**
* Adobe RGB
@@ -221,7 +221,7 @@
* red 0.640 0.330
* white (D65) 0.3127 0.3290</pre>
*/
- STANDARD_ADOBE_RGB = 11 << 16,
+ ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16,
/**
* Transfer aspect
@@ -236,7 +236,7 @@
* component. Implementation may apply the transfer function in RGB space
* for all pixel formats if desired.
*/
- TRANSFER_MASK = 31 << 22,
+ ADATASPACE_TRANSFER_MASK = 31 << 22,
/**
* Transfer characteristics are unknown or are determined by the
@@ -244,13 +244,13 @@
*
* Implementations should use the following transfer functions:
*
- * For YCbCr formats: use TRANSFER_SMPTE_170M
- * For RGB formats: use TRANSFER_SRGB
+ * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M
+ * For RGB formats: use ADATASPACE_TRANSFER_SRGB
*
* For all other formats transfer function is undefined, and implementations
* should use an appropriate standard for the data represented.
*/
- TRANSFER_UNSPECIFIED = 0 << 22,
+ ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22,
/**
* Linear transfer.
@@ -260,7 +260,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_LINEAR = 1 << 22,
+ ADATASPACE_TRANSFER_LINEAR = 1 << 22,
/**
* sRGB transfer.
@@ -271,7 +271,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_SRGB = 2 << 22,
+ ADATASPACE_TRANSFER_SRGB = 2 << 22,
/**
* SMPTE 170M transfer.
@@ -282,7 +282,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_SMPTE_170M = 3 << 22,
+ ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22,
/**
* Display gamma 2.2.
@@ -292,7 +292,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_2 = 4 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22,
/**
* Display gamma 2.6.
@@ -302,7 +302,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_6 = 5 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22,
/**
* Display gamma 2.8.
@@ -312,7 +312,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_8 = 6 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22,
/**
* SMPTE ST 2084 (Dolby Perceptual Quantizer).
@@ -328,7 +328,7 @@
* L = 1 corresponds to 10000 cd/m2
* E - corresponding electrical signal</pre>
*/
- TRANSFER_ST2084 = 7 << 22,
+ ADATASPACE_TRANSFER_ST2084 = 7 << 22,
/**
* ARIB STD-B67 Hybrid Log Gamma.
@@ -344,7 +344,7 @@
* to reference white level of 100 cd/m2
* E - corresponding electrical signal</pre>
*/
- TRANSFER_HLG = 8 << 22,
+ ADATASPACE_TRANSFER_HLG = 8 << 22,
/**
* Range aspect
@@ -352,7 +352,7 @@
* Defines the range of values corresponding to the unit range of 0-1.
* This is defined for YCbCr only, but can be expanded to RGB space.
*/
- RANGE_MASK = 7 << 27,
+ ADATASPACE_RANGE_MASK = 7 << 27,
/**
* Range is unknown or are determined by the application. Implementations
@@ -365,13 +365,13 @@
* For all other formats range is undefined, and implementations should use
* an appropriate range for the data represented.
*/
- RANGE_UNSPECIFIED = 0 << 27,
+ ADATASPACE_RANGE_UNSPECIFIED = 0 << 27,
/**
* Full range uses all values for Y, Cb and Cr from
* 0 to 2^b-1, where b is the bit depth of the color format.
*/
- RANGE_FULL = 1 << 27,
+ ADATASPACE_RANGE_FULL = 1 << 27,
/**
* Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
@@ -386,7 +386,7 @@
* Luma (Y) samples should range from 64 to 940, inclusive
* Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
*/
- RANGE_LIMITED = 2 << 27,
+ ADATASPACE_RANGE_LIMITED = 2 << 27,
/**
* Extended range is used for scRGB. Intended for use with
@@ -395,7 +395,7 @@
* color outside the sRGB gamut.
* Used to blend / merge multiple dataspaces on a single display.
*/
- RANGE_EXTENDED = 3 << 27,
+ ADATASPACE_RANGE_EXTENDED = 3 << 27,
/**
* scRGB linear encoding
@@ -410,7 +410,8 @@
*
* Uses extended range, linear transfer and BT.709 standard.
*/
- ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
+ ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR |
+ // ADATASPACE_RANGE_EXTENDED
/**
* sRGB gamma encoding
@@ -425,7 +426,8 @@
*
* Uses full range, sRGB transfer BT.709 standard.
*/
- ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
+ ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_FULL
/**
* scRGB
@@ -440,14 +442,16 @@
*
* Uses extended range, sRGB transfer and BT.709 standard.
*/
- ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED
+ ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_EXTENDED
/**
* Display P3
*
* Uses full range, sRGB transfer and D65 DCI-P3 standard.
*/
- ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
+ ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -456,7 +460,8 @@
*
* Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+ ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -465,7 +470,8 @@
*
* Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084
+ // | ADATASPACE_RANGE_LIMITED
/**
* Adobe RGB
@@ -475,7 +481,8 @@
* Note: Application is responsible for gamma encoding the data as
* a 2.2 gamma encoding is not supported in HW.
*/
- ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+ ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB |
+ // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL
/**
* JPEG File Interchange Format (JFIF)
@@ -484,7 +491,8 @@
*
* Uses full range, SMPTE 170M transfer and BT.601_625 standard.
*/
- ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
+ ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 601 (BT.601) - 625-line
@@ -493,7 +501,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.601_625 standard.
*/
- ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 |
+ // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED
/**
* ITU-R Recommendation 601 (BT.601) - 525-line
@@ -502,7 +511,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
- ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 |
+ // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -511,7 +521,8 @@
*
* Uses full range, SMPTE 170M transfer and BT2020 standard.
*/
- ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+ ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 709 (BT.709)
@@ -520,7 +531,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.709 standard.
*/
- ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_LIMITED
/**
* SMPTE EG 432-1 and SMPTE RP 431-2
@@ -532,7 +544,8 @@
* Note: Application is responsible for gamma encoding the data as
* a 2.6 gamma encoding is not supported in HW.
*/
- ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+ ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 |
+ // ADATASPACE_RANGE_FULL
/**
* sRGB linear encoding
@@ -546,21 +559,24 @@
*
* Uses full range, linear transfer and BT.709 standard.
*/
- ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+ ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR |
+ // ADATASPACE_RANGE_FULL
/**
* Hybrid Log Gamma encoding
*
* Uses full range, hybrid log gamma transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+ ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
+ // ADATASPACE_RANGE_FULL
/**
* ITU Hybrid Log Gamma encoding
*
* Uses limited range, hybrid log gamma transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
+ // ADATASPACE_RANGE_LIMITED
/**
* Depth
@@ -574,7 +590,37 @@
*
* Embedded depth metadata following the dynamic depth specification.
*/
- ADATASPACE_DYNAMIC_DEPTH = 4098
+ ADATASPACE_DYNAMIC_DEPTH = 4098,
+
+#ifndef ADATASPACE_SKIP_LEGACY_DEFINES
+ STANDARD_MASK = ADATASPACE_STANDARD_MASK,
+ STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED,
+ STANDARD_BT709 = ADATASPACE_STANDARD_BT709,
+ STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625,
+ STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED,
+ STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525,
+ STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED,
+ STANDARD_BT470M = ADATASPACE_STANDARD_BT470M,
+ STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020,
+ STANDARD_FILM = ADATASPACE_STANDARD_FILM,
+ STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3,
+ STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB,
+ TRANSFER_MASK = ADATASPACE_TRANSFER_MASK,
+ TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED,
+ TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR,
+ TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M,
+ TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2,
+ TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6,
+ TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8,
+ TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB,
+ TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084,
+ TRANSFER_HLG = ADATASPACE_TRANSFER_HLG,
+ RANGE_MASK = ADATASPACE_RANGE_MASK,
+ RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED,
+ RANGE_FULL = ADATASPACE_RANGE_FULL,
+ RANGE_LIMITED = ADATASPACE_RANGE_LIMITED,
+ RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED,
+#endif
};
__END_DECLS
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 21798d0..e0e30c3 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -46,6 +46,9 @@
#define ANDROID_HARDWARE_BUFFER_H
#include <android/rect.h>
+#define ADATASPACE_SKIP_LEGACY_DEFINES
+#include <android/data_space.h>
+#undef ADATASPACE_SKIP_LEGACY_DEFINES
#include <inttypes.h>
#include <sys/cdefs.h>
diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h
index a252245..68ac7e0 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) {
@@ -97,14 +103,22 @@
binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
reset();
- return ANativeWindow_readFromParcel(parcel, &mWindow);
+ if (__builtin_available(android __ANDROID_API_U__, *)) {
+ return ANativeWindow_readFromParcel(parcel, &mWindow);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
}
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
if (!mWindow) {
return STATUS_BAD_VALUE;
}
- return ANativeWindow_writeToParcel(mWindow, parcel);
+ if (__builtin_available(android __ANDROID_API_U__, *)) {
+ return ANativeWindow_writeToParcel(mWindow, parcel);
+ } else {
+ return STATUS_FAILED_TRANSACTION;
+ }
}
/**
@@ -123,15 +137,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/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b068f48..969a5cf 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1057,7 +1057,12 @@
/**
* This surface will vote for the minimum refresh rate.
*/
- ANATIVEWINDOW_FRAME_RATE_MIN
+ ANATIVEWINDOW_FRAME_RATE_MIN,
+
+ /**
+ * The surface requests a frame rate that is greater than or equal to `frameRate`.
+ */
+ ANATIVEWINDOW_FRAME_RATE_GTE
};
/*
@@ -1090,10 +1095,19 @@
ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL = 3,
/**
+ * Indicates that, as a result of a user interaction, an animation is likely to start.
+ * This category is a signal that a user interaction heuristic determined the need of a
+ * high refresh rate, and is not an explicit request from the app.
+ * As opposed to FRAME_RATE_CATEGORY_HIGH, this vote may be ignored in favor of
+ * more explicit votes.
+ */
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT = 4,
+
+ /**
* Indicates a frame rate suitable for animations that require a high frame rate, which may
* increase smoothness but may also increase power usage.
*/
- ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 5
};
/*
@@ -1103,17 +1117,34 @@
enum {
/**
* Default value. The layer uses its own frame rate specifications, assuming it has any
- * specifications, instead of its parent's.
+ * specifications, instead of its parent's. If it does not have its own frame rate
+ * specifications, it will try to use its parent's. It will propagate its specifications to any
+ * descendants that do not have their own.
+ *
+ * However, FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN on an ancestor layer
+ * supersedes this behavior, meaning that this layer will inherit frame rate specifications
+ * regardless of whether it has its own.
*/
- ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF = 0,
+ ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE = 0,
/**
* The layer's frame rate specifications will propagate to and override those of its descendant
* layers.
- * The layer with this strategy has the ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF
- * behavior for itself.
+ *
+ * The layer itself has the FRAME_RATE_SELECTION_STRATEGY_PROPAGATE behavior.
+ * Thus, ancestor layer that also has the strategy
+ * FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN will override this layer's
+ * frame rate specifications.
*/
ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1,
+
+ /**
+ * The layer's frame rate specifications will not propagate to its descendant
+ * layers, even if the descendant layer has no frame rate specifications.
+ * However, FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN on an ancestor
+ * layer supersedes this behavior.
+ */
+ ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF = 2,
};
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 21931bb..4918601 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -21,6 +21,7 @@
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
+#include <errno.h>
__BEGIN_DECLS
@@ -105,6 +106,76 @@
AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16,
};
+/**
+ * Additional options for AHardwareBuffer_allocate2. These correspond to
+ * android.hardware.graphics.common.ExtendableType
+ */
+typedef struct {
+ const char* _Nonnull name;
+ int64_t value;
+} AHardwareBufferLongOptions;
+
+enum AHardwareBufferStatus : int32_t {
+ /* Success, no error */
+ AHARDWAREBUFFER_STATUS_OK = 0,
+ /* There's insufficient memory to satisfy the request */
+ AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM,
+ /* The given argument is invalid */
+ AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL,
+ /* The requested operation is not supported by the device */
+ AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS,
+ /* An unknown error occurred */
+ AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1),
+};
+
+/**
+ * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options
+ *
+ * If allocation succeeds, the buffer can be used according to the
+ * usage flags specified in its description. If a buffer is used in ways
+ * not compatible with its usage flags, the results are undefined and
+ * may include program termination.
+ *
+ * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride`
+ * is ignored.
+ * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional
+ * string key + long value options that may be specified. May be null if
+ * `additionalOptionsSize` is 0
+ * @param additionalOptionsSize The number of additional options to pass
+ * @param outBuffer The resulting buffer allocation
+ * @return AHARDWAREBUFFER_STATUS_OK on success
+ * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation
+ * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported
+ * by the device
+ * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error
+ * any reason. The returned buffer has a reference count of 1.
+ */
+enum AHardwareBufferStatus AHardwareBuffer_allocate2(
+ const AHardwareBuffer_Desc* _Nonnull desc,
+ const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Queries the dataspace of the given AHardwareBuffer.
+ *
+ * @param buffer The non-null buffer for which to query the Dataspace
+ * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set
+ */
+enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the dataspace of the given AHardwareBuffer
+ * @param buffer The non-null buffer for which to set the dataspace
+ * @param dataSpace The dataspace to set
+ * @return AHARDWAREBUFFER_STATUS_OK on success,
+ * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace,
+ * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure.
+ */
+enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer,
+ enum ADataSpace dataSpace)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
__END_DECLS
#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index dcb5068..8bc1292 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,10 +2,11 @@
global:
AHardwareBuffer_acquire;
AHardwareBuffer_allocate;
- AHardwareBuffer_createFromHandle; # llndk # systemapi
+ AHardwareBuffer_allocate2; # llndk systemapi
+ AHardwareBuffer_createFromHandle; # llndk systemapi
AHardwareBuffer_describe;
AHardwareBuffer_getId; # introduced=31
- AHardwareBuffer_getNativeHandle; # llndk # systemapi
+ AHardwareBuffer_getNativeHandle; # llndk systemapi
AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
AHardwareBuffer_lockAndGetInfo; # introduced=29
@@ -16,6 +17,8 @@
AHardwareBuffer_unlock;
AHardwareBuffer_readFromParcel; # introduced=34
AHardwareBuffer_writeToParcel; # introduced=34
+ AHardwareBuffer_getDataSpace; # llndk systemapi
+ AHardwareBuffer_setDataSpace; # llndk systemapi
ANativeWindowBuffer_getHardwareBuffer; # llndk
ANativeWindow_OemStorageGet; # llndk
ANativeWindow_OemStorageSet; # llndk
@@ -26,18 +29,18 @@
ANativeWindow_getBuffersDefaultDataSpace; # introduced=34
ANativeWindow_getFormat;
ANativeWindow_getHeight;
- ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30
- ANativeWindow_getLastDequeueStartTime; # systemapi # introduced=30
- ANativeWindow_getLastQueueDuration; # systemapi # introduced=30
+ ANativeWindow_getLastDequeueDuration; # systemapi introduced=30
+ ANativeWindow_getLastDequeueStartTime; # systemapi introduced=30
+ ANativeWindow_getLastQueueDuration; # systemapi introduced=30
ANativeWindow_getWidth;
ANativeWindow_lock;
ANativeWindow_query; # llndk
ANativeWindow_queryf; # llndk
ANativeWindow_queueBuffer; # llndk
- ANativeWindow_setCancelBufferInterceptor; # systemapi # introduced=30
- ANativeWindow_setDequeueBufferInterceptor; # systemapi # introduced=30
- ANativeWindow_setPerformInterceptor; # systemapi # introduced=30
- ANativeWindow_setQueueBufferInterceptor; # systemapi # introduced=30
+ ANativeWindow_setCancelBufferInterceptor; # systemapi introduced=30
+ ANativeWindow_setDequeueBufferInterceptor; # systemapi introduced=30
+ ANativeWindow_setPerformInterceptor; # systemapi introduced=30
+ ANativeWindow_setQueueBufferInterceptor; # systemapi introduced=30
ANativeWindow_release;
ANativeWindow_setAutoPrerotation; # llndk
ANativeWindow_setAutoRefresh; # llndk
@@ -48,7 +51,7 @@
ANativeWindow_setBuffersGeometry;
ANativeWindow_setBuffersTimestamp; # llndk
ANativeWindow_setBuffersTransform;
- ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
+ ANativeWindow_setDequeueTimeout; # systemapi introduced=30
ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index ef863b6..1f0128a 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "AHardwareBuffer_test"
//#define LOG_NDEBUG 0
+#include <android-base/properties.h>
+#include <android/data_space.h>
#include <android/hardware/graphics/common/1.0/types.h>
#include <gtest/gtest.h>
#include <private/android/AHardwareBufferHelpers.h>
@@ -26,6 +28,10 @@
using namespace android;
using android::hardware::graphics::common::V1_0::BufferUsage;
+static bool IsCuttlefish() {
+ return ::android::base::GetProperty("ro.product.board", "") == "cutf";
+}
+
static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
uint64_t actual, const char* type) {
std::ostringstream ss;
@@ -170,3 +176,83 @@
EXPECT_NE(id1, id2);
}
+
+TEST(AHardwareBufferTest, Allocate2NoExtras) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, nullptr, 0, &buffer));
+ uint64_t id = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id));
+ EXPECT_NE(0, id);
+ AHardwareBuffer_Desc desc2{};
+ AHardwareBuffer_describe(buffer, &desc2);
+ EXPECT_EQ(desc.width, desc2.width);
+ EXPECT_EQ(desc.height, desc2.height);
+ EXPECT_GE(desc2.stride, desc2.width);
+
+ AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, Allocate2WithExtras) {
+ if (!IsCuttlefish()) {
+ GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras";
+ }
+
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 48,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ std::array<AHardwareBufferLongOptions, 1> extras = {{
+ {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
+ }};
+ ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, extras.data(), extras.size(), &buffer));
+ uint64_t id = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id));
+ EXPECT_NE(0, id);
+ AHardwareBuffer_Desc desc2{};
+ AHardwareBuffer_describe(buffer, &desc2);
+ EXPECT_EQ(desc.width, desc2.width);
+ EXPECT_EQ(desc.height, desc2.height);
+ EXPECT_GE(desc2.stride, desc2.width);
+
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer));
+
+ AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, GetSetDataspace) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 48,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer));
+
+ EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer));
+ AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3);
+ if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) {
+ EXPECT_EQ(0, status);
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer));
+ }
+
+ AHardwareBuffer_release(buffer);
+}
\ No newline at end of file
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index 30737c1..d7c7eb3 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -31,6 +31,7 @@
"device-tests",
],
shared_libs: [
+ "libbase",
"libgui",
"liblog",
"libnativewindow",
@@ -44,5 +45,8 @@
"ANativeWindowTest.cpp",
"c_compatibility.c",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index cd1ac2b..92fe4c0 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -132,8 +132,8 @@
"\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
"texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
" colorType %i",
- msg, tex.isValid(), dataspace, tex.width(), tex.height(),
- tex.hasMipmaps(), tex.isProtected(),
+ msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
+ tex.height(), tex.hasMipmaps(), tex.isProtected(),
static_cast<int>(tex.textureType()), retrievedTextureInfo,
textureInfo.fTarget, textureInfo.fFormat, colorType);
break;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 2053c6a..d688b51 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -236,7 +236,13 @@
err = selectEGLConfig(display, format, 0, &config);
if (err != NO_ERROR) {
// this EGL is too lame for android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"
+ " (format: %d, vendor: %s, version: %s, extensions: %s, Client"
+ " API: %s)",
+ format, eglQueryString(display, EGL_VENDOR),
+ eglQueryString(display, EGL_VERSION),
+ eglQueryString(display, EGL_EXTENSIONS),
+ eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
}
}
}
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 8821c0e..ba20d1f 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -433,6 +433,10 @@
// Looks like this would slow things down and we can't depend on it on all platforms
interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE;
+ if (protectedContent && !interface.protectedMemoryFeatures->protectedMemory) {
+ BAIL("Protected memory not supported");
+ }
+
float queuePriorities[1] = {0.0f};
void* queueNextPtr = nullptr;
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 367bee8..f58f543 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -125,8 +125,10 @@
}
void RenderEngineThreaded::waitUntilInitialized() const {
- std::unique_lock<std::mutex> lock(mInitializedMutex);
- mInitializedCondition.wait(lock, [=] { return mIsInitialized; });
+ if (!mIsInitialized) {
+ std::unique_lock<std::mutex> lock(mInitializedMutex);
+ mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); });
+ }
}
std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 74af2bd..3f1e67f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -97,7 +97,7 @@
// Used to allow select thread safe methods to be accessed without requiring the
// method to be invoked on the RenderEngine thread
- bool mIsInitialized = false;
+ std::atomic_bool mIsInitialized = false;
mutable std::mutex mInitializedMutex;
mutable std::condition_variable mInitializedCondition;
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index d112a12..f8ee3fc 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -248,6 +248,22 @@
return static_cast<ssize_t>(mSensors.size());
}
+ssize_t SensorManager::getDefaultDeviceSensorList(Vector<Sensor> & list) {
+ Mutex::Autolock _l(mLock);
+ status_t err = assertStateLocked();
+ if (err < 0) {
+ return static_cast<ssize_t>(err);
+ }
+
+ if (mDeviceId == DEVICE_ID_DEFAULT) {
+ list = mSensors;
+ } else {
+ list = mSensorServer->getSensorList(mOpPackageName);
+ }
+
+ return static_cast<ssize_t>(list.size());
+}
+
ssize_t SensorManager::getDynamicSensorList(Vector<Sensor> & dynamicSensors) {
Mutex::Autolock _l(mLock);
status_t err = assertStateLocked();
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index e67fac7..49f050a 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -58,6 +58,7 @@
~SensorManager();
ssize_t getSensorList(Sensor const* const** list);
+ ssize_t getDefaultDeviceSensorList(Vector<Sensor> & list);
ssize_t getDynamicSensorList(Vector<Sensor>& list);
ssize_t getDynamicSensorList(Sensor const* const** list);
ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list);
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/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index e9b5dec..a5aca99 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -384,8 +384,8 @@
status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
@@ -400,6 +400,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 474d381..152b35a 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -371,7 +371,7 @@
status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -383,6 +383,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index b6274ab..d6970e0 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -468,8 +468,8 @@
uint32_t layerCount, uint64_t usage,
bool* outSupported) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- if (auto error = sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage,
- &descriptorInfo) != OK) {
+ if (sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage,
+ &descriptorInfo) != OK) {
// Usage isn't known to the HAL or otherwise failed validation.
*outSupported = false;
return OK;
@@ -1069,7 +1069,7 @@
status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage,
@@ -1084,6 +1084,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
if (mAidlAllocator) {
AllocationResult result;
#pragma clang diagnostic push
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index 37ebfc4..b07e155 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -19,6 +19,7 @@
#include <ui/Gralloc5.h>
+#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
@@ -215,55 +216,75 @@
status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t *outStride,
- buffer_handle_t *outBufferHandles, bool importBuffers) const {
- auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage);
+ uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ auto result = allocate(GraphicBufferAllocator::AllocationRequest{
+ .importBuffer = importBuffers,
+ .width = width,
+ .height = height,
+ .format = format,
+ .layerCount = layerCount,
+ .usage = usage,
+ .requestorName = requestorName,
+ });
+
+ *outStride = result.stride;
+ outBufferHandles[0] = result.handle;
+ return result.status;
+}
+
+GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate(
+ const GraphicBufferAllocator::AllocationRequest& request) const {
+ auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height,
+ request.format, request.layerCount, request.usage);
if (!descriptorInfo) {
- return BAD_VALUE;
+ return GraphicBufferAllocator::AllocationResult{BAD_VALUE};
+ }
+
+ descriptorInfo->additionalOptions.reserve(request.extras.size());
+ for (const auto& option : request.extras) {
+ ExtendableType type;
+ type.name = option.name;
+ type.value = option.value;
+ descriptorInfo->additionalOptions.push_back(std::move(type));
}
AllocationResult result;
- auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result);
+ auto status = mAllocator->allocate2(*descriptorInfo, 1, &result);
if (!status.isOk()) {
auto error = status.getExceptionCode();
if (error == EX_SERVICE_SPECIFIC) {
- error = status.getServiceSpecificError();
+ switch (static_cast<AllocationError>(status.getServiceSpecificError())) {
+ case AllocationError::BAD_DESCRIPTOR:
+ error = BAD_VALUE;
+ break;
+ case AllocationError::NO_RESOURCES:
+ error = NO_MEMORY;
+ break;
+ default:
+ error = UNKNOWN_ERROR;
+ break;
+ }
}
- if (error == OK) {
- error = UNKNOWN_ERROR;
- }
- return error;
+ return GraphicBufferAllocator::AllocationResult{error};
}
- if (importBuffers) {
- for (uint32_t i = 0; i < bufferCount; i++) {
- auto handle = makeFromAidl(result.buffers[i]);
- auto error = mMapper.importBuffer(handle, &outBufferHandles[i]);
- native_handle_delete(handle);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
- }
- return error;
- }
+ GraphicBufferAllocator::AllocationResult ret{OK};
+ if (request.importBuffer) {
+ auto handle = makeFromAidl(result.buffers[0]);
+ auto error = mMapper.importBuffer(handle, &ret.handle);
+ native_handle_delete(handle);
+ if (error != NO_ERROR) {
+ return GraphicBufferAllocator::AllocationResult{error};
}
} else {
- for (uint32_t i = 0; i < bufferCount; i++) {
- outBufferHandles[i] = dupFromAidl(result.buffers[i]);
- if (!outBufferHandles[i]) {
- for (uint32_t j = 0; j < i; j++) {
- auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]);
- native_handle_close(buffer);
- native_handle_delete(buffer);
- outBufferHandles[j] = nullptr;
- }
- return NO_MEMORY;
- }
+ ret.handle = dupFromAidl(result.buffers[0]);
+ if (!ret.handle) {
+ return GraphicBufferAllocator::AllocationResult{NO_MEMORY};
}
}
- *outStride = result.stride;
+ ret.stride = result.stride;
// Release all the resources held by AllocationResult (specifically any remaining FDs)
result = {};
@@ -272,7 +293,7 @@
// is marked apex_available (b/214400477) and libbinder isn't (which of course is correct)
// IPCThreadState::self()->flushCommands();
- return OK;
+ return ret;
}
void Gralloc5Mapper::preload() {
@@ -518,14 +539,16 @@
}
}
{
- auto value =
- getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper,
- bufferHandle);
- if (static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format) !=
- value) {
- ALOGW("Format didn't match, expected %d got %s", format,
- value.has_value() ? toString(*value).c_str() : "<null>");
- return BAD_VALUE;
+ auto expected = static_cast<APixelFormat>(format);
+ if (expected != APixelFormat::IMPLEMENTATION_DEFINED) {
+ auto value =
+ getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper,
+ bufferHandle);
+ if (expected != value) {
+ ALOGW("Format didn't match, expected %d got %s", format,
+ value.has_value() ? toString(*value).c_str() : "<null>");
+ return BAD_VALUE;
+ }
}
}
{
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 429760f..c007fdb 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -106,6 +106,26 @@
inUsage, inStride);
}
+GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request)
+ : GraphicBuffer() {
+ GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
+ auto result = allocator.allocate(request);
+ mInitCheck = result.status;
+ if (result.status == NO_ERROR) {
+ handle = result.handle;
+ stride = result.stride;
+
+ mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
+
+ width = static_cast<int>(request.width);
+ height = static_cast<int>(request.height);
+ format = request.format;
+ layerCount = request.layerCount;
+ usage = request.usage;
+ usage_deprecated = int(usage);
+ }
+}
+
GraphicBuffer::~GraphicBuffer()
{
ATRACE_CALL();
@@ -143,6 +163,10 @@
const_cast<GraphicBuffer*>(this));
}
+status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const {
+ return mBufferMapper.getDataspace(handle, outDataspace);
+}
+
status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
{
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index eb0bd4e..98082fb 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -113,6 +113,79 @@
ALOGD("%s", s.c_str());
}
+auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult {
+ ATRACE_CALL();
+ if (!request.width || !request.height) {
+ return AllocationResult(BAD_VALUE);
+ }
+
+ const auto width = request.width;
+ const auto height = request.height;
+
+ const uint32_t bpp = bytesPerPixel(request.format);
+ if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": Requesting too large a buffer size",
+ request.width, request.height, request.layerCount, request.format, request.usage);
+ return AllocationResult(BAD_VALUE);
+ }
+
+ if (request.layerCount < 1) {
+ return AllocationResult(BAD_VALUE);
+ }
+
+ auto result = mAllocator->allocate(request);
+ if (result.status == UNKNOWN_TRANSACTION) {
+ if (!request.extras.empty()) {
+ ALOGE("Failed to allocate with additional options, allocator version mis-match? "
+ "gralloc version = %d",
+ (int)mMapper.getMapperVersion());
+ return result;
+ }
+ // If there's no additional options, fall back to previous allocate version
+ result.status = mAllocator->allocate(request.requestorName, request.width, request.height,
+ request.format, request.layerCount, request.usage,
+ &result.stride, &result.handle, request.importBuffer);
+ }
+
+ if (result.status != NO_ERROR) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": %d",
+ request.width, request.height, request.layerCount, request.format, request.usage,
+ result.status);
+ return result;
+ }
+
+ if (!request.importBuffer) {
+ return result;
+ }
+ size_t bufSize;
+
+ // if stride has no meaning or is too large,
+ // approximate size with the input width instead
+ if ((result.stride) != 0 &&
+ std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) {
+ bufSize = static_cast<size_t>(width) * height * bpp;
+ } else {
+ bufSize = static_cast<size_t>((result.stride)) * height * bpp;
+ }
+
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.width = width;
+ rec.height = height;
+ rec.stride = result.stride;
+ rec.format = request.format;
+ rec.layerCount = request.layerCount;
+ rec.usage = request.usage;
+ rec.size = bufSize;
+ rec.requestorName = request.requestorName;
+ list.add(result.handle, rec);
+
+ return result;
+}
+
status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride,
@@ -141,7 +214,7 @@
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
- 1, stride, handle, importBuffer);
+ stride, handle, importBuffer);
if (error != NO_ERROR) {
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
diff --git a/libs/ui/include/ui/DisplayMap.h b/libs/ui/include/ui/DisplayMap.h
index 7eacb0a..65d2b8f 100644
--- a/libs/ui/include/ui/DisplayMap.h
+++ b/libs/ui/include/ui/DisplayMap.h
@@ -23,13 +23,18 @@
// The static capacities were chosen to exceed a typical number of physical and/or virtual displays.
+constexpr size_t kDisplayCapacity = 5;
template <typename Key, typename Value>
-using DisplayMap = ftl::SmallMap<Key, Value, 5>;
+using DisplayMap = ftl::SmallMap<Key, Value, kDisplayCapacity>;
+constexpr size_t kPhysicalDisplayCapacity = 3;
template <typename Key, typename Value>
-using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+using PhysicalDisplayMap = ftl::SmallMap<Key, Value, kPhysicalDisplayCapacity>;
template <typename T>
-using PhysicalDisplayVector = ftl::SmallVector<T, 3>;
+using DisplayVector = ftl::SmallVector<T, kDisplayCapacity>;
+
+template <typename T>
+using PhysicalDisplayVector = ftl::SmallVector<T, kPhysicalDisplayCapacity>;
} // namespace android::ui
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 496ba57..e6015e0 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -23,6 +23,7 @@
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include "GraphicBufferAllocator.h"
#include <string>
@@ -218,9 +219,13 @@
*/
virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
bool importBuffers = true) const = 0;
+
+ virtual GraphicBufferAllocator::AllocationResult allocate(
+ const GraphicBufferAllocator::AllocationRequest&) const {
+ return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION);
+ }
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index a7b6f492..e50bb3a 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -81,9 +81,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 7367549..035684a 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -82,9 +82,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index df43be8..0f469c0 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -174,9 +174,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h
index 44b97d1..f9e8f5e 100644
--- a/libs/ui/include/ui/Gralloc5.h
+++ b/libs/ui/include/ui/Gralloc5.h
@@ -172,10 +172,12 @@
[[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t *outStride,
- buffer_handle_t *outBufferHandles,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
bool importBuffers) const override;
+ [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate(
+ const GraphicBufferAllocator::AllocationRequest&) const override;
+
private:
const Gralloc5Mapper &mMapper;
std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator;
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index f859848..652d8ba 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -26,6 +26,7 @@
#include <android/hardware_buffer.h>
#include <ui/ANativeObjectBase.h>
+#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
@@ -103,6 +104,8 @@
uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName = "<Unknown>");
+ GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&);
+
// Create a GraphicBuffer from an existing handle.
enum HandleWrapMethod : uint8_t {
// Wrap and use the handle directly. It assumes the handle has been
@@ -169,6 +172,8 @@
mGenerationNumber = generation;
}
+ status_t getDataspace(ui::Dataspace* outDataspace) const;
+
// This function is privileged. It requires access to the allocator
// device or service, which usually involves adding suitable selinux
// rules.
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 3ed988c..8f461e1 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -22,6 +22,7 @@
#include <memory>
#include <string>
+#include <vector>
#include <cutils/native_handle.h>
@@ -42,6 +43,35 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
+ struct AdditionalOptions {
+ const char* name;
+ int64_t value;
+ };
+
+ struct AllocationRequest {
+ bool importBuffer;
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint32_t layerCount;
+ uint64_t usage;
+ std::string requestorName;
+ std::vector<AdditionalOptions> extras;
+ };
+
+ struct AllocationResult {
+ status_t status;
+ buffer_handle_t handle = nullptr;
+ uint32_t stride = 0;
+
+ explicit AllocationResult(status_t status) : status(status) {}
+
+ explicit AllocationResult(buffer_handle_t handle, uint32_t stride)
+ : status(OK), handle(handle), stride(stride) {}
+ };
+
+ AllocationResult allocate(const AllocationRequest&);
+
/**
* Allocates and imports a gralloc buffer.
*
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index f4c0afa..efca083 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
std::cout << "Setting expected stride to " << stride << std::endl;
EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
allocate)
- .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
+ .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
}
std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
};
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index d62e3e2..d02b387 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -35,7 +35,7 @@
MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
MOCK_METHOD(status_t, allocate,
(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint32_t layerCount, uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
index 9deba01..eda5ea4 100644
--- a/libs/ultrahdr/Android.bp
+++ b/libs/ultrahdr/Android.bp
@@ -21,7 +21,8 @@
}
cc_library {
- name: "libultrahdr",
+ name: "libultrahdr-deprecated",
+ enabled: false,
host_supported: true,
vendor_available: true,
export_include_dirs: ["include"],
@@ -46,7 +47,8 @@
}
cc_library {
- name: "libjpegencoder",
+ name: "libjpegencoder-deprecated",
+ enabled: false,
host_supported: true,
vendor_available: true,
@@ -64,7 +66,8 @@
}
cc_library {
- name: "libjpegdecoder",
+ name: "libjpegdecoder-deprecated",
+ enabled: false,
host_supported: true,
vendor_available: true,
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
index e999a8b..2fa361f 100644
--- a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
+++ b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
license {
- name: "adobe_hdr_gain_map_license",
+ name: "adobe_hdr_gain_map_license-deprecated",
license_kinds: ["legacy_by_exception_only"],
license_text: ["NOTICE"],
}
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
index 6c0a2f5..8d9132f 100644
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ b/libs/ultrahdr/fuzzer/Android.bp
@@ -22,7 +22,8 @@
}
cc_defaults {
- name: "ultrahdr_fuzzer_defaults",
+ name: "ultrahdr_fuzzer_defaults-deprecated",
+ enabled: false,
host_supported: true,
shared_libs: [
"libimage_io",
@@ -53,7 +54,8 @@
}
cc_fuzz {
- name: "ultrahdr_enc_fuzzer",
+ name: "ultrahdr_enc_fuzzer-deprecated",
+ enabled: false,
defaults: ["ultrahdr_fuzzer_defaults"],
srcs: [
"ultrahdr_enc_fuzzer.cpp",
@@ -61,7 +63,8 @@
}
cc_fuzz {
- name: "ultrahdr_dec_fuzzer",
+ name: "ultrahdr_dec_fuzzer-deprecated",
+ enabled: false,
defaults: ["ultrahdr_fuzzer_defaults"],
srcs: [
"ultrahdr_dec_fuzzer.cpp",
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
index bda804a..00cc797 100644
--- a/libs/ultrahdr/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -22,7 +22,8 @@
}
cc_test {
- name: "ultrahdr_unit_test",
+ name: "ultrahdr_unit_test-deprecated",
+ enabled: false,
test_suites: ["device-tests"],
srcs: [
"gainmapmath_test.cpp",
diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp
index f2a313c..cb063af 100644
--- a/libs/vibrator/fuzzer/Android.bp
+++ b/libs/vibrator/fuzzer/Android.bp
@@ -47,6 +47,17 @@
],
fuzz_config: {
- componentid: 155276,
+ cc: [
+ "android-haptics@google.com",
+ ],
+ componentid: 345036,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libvibrator",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
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/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index ed3c616..9905210 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -18,6 +18,7 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -62,12 +63,15 @@
namespace android {
MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
- const std::string& baseDir)
+ size_t maxTotalEntries, const std::string& baseDir)
: mInitialized(false),
+ mCacheVersion(0),
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
mMaxTotalSize(maxTotalSize),
+ mMaxTotalEntries(maxTotalEntries),
mTotalCacheSize(0),
+ mTotalCacheEntries(0),
mHotCacheLimit(0),
mHotCacheSize(0),
mWorkerThreadIdle(true) {
@@ -76,6 +80,26 @@
return;
}
+ // Set the cache version, override if debug value set
+ mCacheVersion = kMultifileBlobCacheVersion;
+ int debugCacheVersion = base::GetIntProperty("debug.egl.blobcache.cache_version", -1);
+ if (debugCacheVersion >= 0) {
+ ALOGV("INIT: Using %u as cacheVersion instead of %u", debugCacheVersion, mCacheVersion);
+ mCacheVersion = debugCacheVersion;
+ }
+
+ // Set the platform build ID, override if debug value set
+ mBuildId = base::GetProperty("ro.build.id", "");
+ std::string debugBuildId = base::GetProperty("debug.egl.blobcache.build_id", "");
+ if (!debugBuildId.empty()) {
+ ALOGV("INIT: Using %s as buildId instead of %s", debugBuildId.c_str(), mBuildId.c_str());
+ if (debugBuildId.length() > PROP_VALUE_MAX) {
+ ALOGV("INIT: debugBuildId is too long (%zu), reduce it to %u", debugBuildId.length(),
+ PROP_VALUE_MAX);
+ }
+ mBuildId = debugBuildId;
+ }
+
// Establish the name of our multifile directory
mMultifileDirName = baseDir + ".multifile";
@@ -93,14 +117,30 @@
mTaskThread = std::thread(&MultifileBlobCache::processTasks, this);
// See if the dir exists, and initialize using its contents
+ bool statusGood = false;
+
+ // Check that our cacheVersion and buildId match
struct stat st;
if (stat(mMultifileDirName.c_str(), &st) == 0) {
+ if (checkStatus(mMultifileDirName.c_str())) {
+ statusGood = true;
+ } else {
+ ALOGV("INIT: Cache status has changed, clearing the cache");
+ if (!clearCache()) {
+ ALOGE("INIT: Unable to clear cache");
+ return;
+ }
+ }
+ }
+
+ if (statusGood) {
// Read all the files and gather details, then preload their contents
DIR* dir;
struct dirent* entry;
if ((dir = opendir(mMultifileDirName.c_str())) != nullptr) {
while ((entry = readdir(dir)) != nullptr) {
- if (entry->d_name == "."s || entry->d_name == ".."s) {
+ if (entry->d_name == "."s || entry->d_name == ".."s ||
+ strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
@@ -123,7 +163,8 @@
if (st.st_size <= 0 || st.st_atime <= 0) {
ALOGE("INIT: Entry %u has invalid stats! Removing.", entryHash);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
continue;
}
@@ -140,7 +181,7 @@
MultifileHeader header;
size_t result = read(fd, static_cast<void*>(&header), sizeof(MultifileHeader));
if (result != sizeof(MultifileHeader)) {
- ALOGE("Error reading MultifileHeader from cache entry (%s): %s",
+ ALOGE("INIT: Error reading MultifileHeader from cache entry (%s): %s",
fullPath.c_str(), std::strerror(errno));
close(fd);
return;
@@ -150,7 +191,8 @@
if (header.magic != kMultifileMagic) {
ALOGE("INIT: Entry %u has bad magic (%u)! Removing.", entryHash, header.magic);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
close(fd);
continue;
@@ -175,7 +217,7 @@
if (header.crc !=
crc32c(mappedEntry + sizeof(MultifileHeader),
fileSize - sizeof(MultifileHeader))) {
- ALOGE("INIT: Entry %u failed CRC check! Removing.", entryHash);
+ ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash);
if (remove(fullPath.c_str()) != 0) {
ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
}
@@ -184,11 +226,12 @@
// If the cache entry is damaged or no good, remove it
if (header.keySize <= 0 || header.valueSize <= 0) {
- ALOGE("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
+ ALOGV("INIT: Entry %u has a bad header keySize (%lu) or valueSize (%lu), "
"removing.",
entryHash, header.keySize, header.valueSize);
if (remove(fullPath.c_str()) != 0) {
- ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ ALOGE("INIT: Error removing %s: %s", fullPath.c_str(),
+ std::strerror(errno));
}
continue;
}
@@ -226,9 +269,17 @@
// If the multifile directory does not exist, create it and start from scratch
if (mkdir(mMultifileDirName.c_str(), 0755) != 0 && (errno != EEXIST)) {
ALOGE("Unable to create directory (%s), errno (%i)", mMultifileDirName.c_str(), errno);
+ return;
+ }
+
+ // Create new status file
+ if (!createStatus(mMultifileDirName.c_str())) {
+ ALOGE("INIT: Failed to create status file!");
+ return;
}
}
+ ALOGV("INIT: Multifile BlobCache initialization succeeded");
mInitialized = true;
}
@@ -270,7 +321,7 @@
size_t fileSize = sizeof(MultifileHeader) + keySize + valueSize;
// If we're going to be over the cache limit, kick off a trim to clear space
- if (getTotalSize() + fileSize > mMaxTotalSize) {
+ if (getTotalSize() + fileSize > mMaxTotalSize || getTotalEntries() + 1 > mMaxTotalEntries) {
ALOGV("SET: Cache is full, calling trimCache to clear space");
trimCache();
}
@@ -469,6 +520,112 @@
}
}
+bool MultifileBlobCache::createStatus(const std::string& baseDir) {
+ // Populate the status struct
+ struct MultifileStatus status;
+ memset(&status, 0, sizeof(status));
+ status.magic = kMultifileMagic;
+ status.cacheVersion = mCacheVersion;
+
+ // Copy the buildId string in, up to our allocated space
+ strncpy(status.buildId, mBuildId.c_str(),
+ mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length());
+
+ // Finally update the crc, using cacheVersion and everything the follows
+ status.crc =
+ crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
+ sizeof(status) - offsetof(MultifileStatus, cacheVersion));
+
+ // Create the status file
+ std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
+ int fd = open(cacheStatus.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ALOGE("STATUS(CREATE): Unable to create status file: %s, error: %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ // Write the buffer contents to disk
+ ssize_t result = write(fd, &status, sizeof(status));
+ close(fd);
+ if (result != sizeof(status)) {
+ ALOGE("STATUS(CREATE): Error writing cache status file: %s, error %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ ALOGV("STATUS(CREATE): Created status file: %s", cacheStatus.c_str());
+ return true;
+}
+
+bool MultifileBlobCache::checkStatus(const std::string& baseDir) {
+ std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile;
+
+ // Does status exist
+ struct stat st;
+ if (stat(cacheStatus.c_str(), &st) != 0) {
+ ALOGV("STATUS(CHECK): Status file (%s) missing", cacheStatus.c_str());
+ return false;
+ }
+
+ // If the status entry is damaged or no good, remove it
+ if (st.st_size <= 0 || st.st_atime <= 0) {
+ ALOGE("STATUS(CHECK): Cache status has invalid stats!");
+ return false;
+ }
+
+ // Open the file so we can read its header
+ int fd = open(cacheStatus.c_str(), O_RDONLY);
+ if (fd == -1) {
+ ALOGE("STATUS(CHECK): Cache error - failed to open cacheStatus: %s, error: %s",
+ cacheStatus.c_str(), std::strerror(errno));
+ return false;
+ }
+
+ // Read in the status header
+ MultifileStatus status;
+ size_t result = read(fd, static_cast<void*>(&status), sizeof(MultifileStatus));
+ close(fd);
+ if (result != sizeof(MultifileStatus)) {
+ ALOGE("STATUS(CHECK): Error reading cache status (%s): %s", cacheStatus.c_str(),
+ std::strerror(errno));
+ return false;
+ }
+
+ // Verify header magic
+ if (status.magic != kMultifileMagic) {
+ ALOGE("STATUS(CHECK): Cache status has bad magic (%u)!", status.magic);
+ return false;
+ }
+
+ // Ensure we have a good CRC
+ if (status.crc !=
+ crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion),
+ sizeof(status) - offsetof(MultifileStatus, cacheVersion))) {
+ ALOGE("STATUS(CHECK): Cache status failed CRC check!");
+ return false;
+ }
+
+ // Check cacheVersion
+ if (status.cacheVersion != mCacheVersion) {
+ ALOGV("STATUS(CHECK): Cache version has changed! old(%u) new(%u)", status.cacheVersion,
+ mCacheVersion);
+ return false;
+ }
+
+ // Check buildId
+ if (strcmp(status.buildId, mBuildId.c_str()) != 0) {
+ ALOGV("STATUS(CHECK): BuildId has changed! old(%s) new(%s)", status.buildId,
+ mBuildId.c_str());
+ return false;
+ }
+
+ // All checks passed!
+ ALOGV("STATUS(CHECK): Status file is good! cacheVersion(%u), buildId(%s) file(%s)",
+ status.cacheVersion, status.buildId, cacheStatus.c_str());
+ return true;
+}
+
void MultifileBlobCache::trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
time_t accessTime) {
mEntries.insert(entryHash);
@@ -485,10 +642,12 @@
void MultifileBlobCache::increaseTotalCacheSize(size_t fileSize) {
mTotalCacheSize += fileSize;
+ mTotalCacheEntries++;
}
void MultifileBlobCache::decreaseTotalCacheSize(size_t fileSize) {
mTotalCacheSize -= fileSize;
+ mTotalCacheEntries--;
}
bool MultifileBlobCache::addToHotCache(uint32_t newEntryHash, int newFd, uint8_t* newEntryBuffer,
@@ -557,7 +716,7 @@
return false;
}
-bool MultifileBlobCache::applyLRU(size_t cacheLimit) {
+bool MultifileBlobCache::applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit) {
// Walk through our map of sorted last access times and remove files until under the limit
for (auto cacheEntryIter = mEntryStats.begin(); cacheEntryIter != mEntryStats.end();) {
uint32_t entryHash = cacheEntryIter->first;
@@ -590,9 +749,10 @@
// See if it has been reduced enough
size_t totalCacheSize = getTotalSize();
- if (totalCacheSize <= cacheLimit) {
+ size_t totalCacheEntries = getTotalEntries();
+ if (totalCacheSize <= cacheSizeLimit && totalCacheEntries <= cacheEntryLimit) {
// Success
- ALOGV("LRU: Reduced cache to %zu", totalCacheSize);
+ ALOGV("LRU: Reduced cache to size %zu entries %zu", totalCacheSize, totalCacheEntries);
return true;
}
}
@@ -601,8 +761,43 @@
return false;
}
+// Clear the cache by removing all entries and deleting the directory
+bool MultifileBlobCache::clearCache() {
+ DIR* dir;
+ struct dirent* entry;
+ dir = opendir(mMultifileDirName.c_str());
+ if (dir == nullptr) {
+ ALOGE("CLEAR: Unable to open multifile dir: %s", mMultifileDirName.c_str());
+ return false;
+ }
+
+ // Delete all entries and the status file
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+
+ std::string entryName = entry->d_name;
+ std::string fullPath = mMultifileDirName + "/" + entryName;
+ if (remove(fullPath.c_str()) != 0) {
+ ALOGE("CLEAR: Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
+ return false;
+ }
+ }
+
+ // Delete the directory
+ if (remove(mMultifileDirName.c_str()) != 0) {
+ ALOGE("CLEAR: Error removing %s: %s", mMultifileDirName.c_str(), std::strerror(errno));
+ return false;
+ }
+
+ ALOGV("CLEAR: Cleared the multifile blobcache");
+ return true;
+}
+
// When removing files, what fraction of the overall limit should be reached when removing files
// A divisor of two will decrease the cache to 50%, four to 25% and so on
+// We use the same limit to manage size and entry count
constexpr uint32_t kCacheLimitDivisor = 2;
// Calculate the cache size and remove old entries until under the limit
@@ -611,8 +806,9 @@
ALOGV("TRIM: Waiting for work to complete.");
waitForWorkComplete();
- ALOGV("TRIM: Reducing multifile cache size to %zu", mMaxTotalSize / kCacheLimitDivisor);
- if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor)) {
+ ALOGV("TRIM: Reducing multifile cache size to %zu, entries %zu",
+ mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor);
+ if (!applyLRU(mMaxTotalSize / kCacheLimitDivisor, mMaxTotalEntries / kCacheLimitDivisor)) {
ALOGE("Error when clearing multifile shader cache");
return;
}
diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h
index 5e527dc..18566c2 100644
--- a/opengl/libs/EGL/MultifileBlobCache.h
+++ b/opengl/libs/EGL/MultifileBlobCache.h
@@ -21,6 +21,7 @@
#include <EGL/eglext.h>
#include <android-base/thread_annotations.h>
+#include <cutils/properties.h>
#include <future>
#include <map>
#include <queue>
@@ -33,6 +34,9 @@
namespace android {
+constexpr uint32_t kMultifileBlobCacheVersion = 1;
+constexpr char kMultifileBlobCacheStatusFile[] = "cache.status";
+
struct MultifileHeader {
uint32_t magic;
uint32_t crc;
@@ -46,6 +50,13 @@
time_t accessTime;
};
+struct MultifileStatus {
+ uint32_t magic;
+ uint32_t crc;
+ uint32_t cacheVersion;
+ char buildId[PROP_VALUE_MAX];
+};
+
struct MultifileHotCache {
int entryFd;
uint8_t* entryBuffer;
@@ -92,7 +103,7 @@
class MultifileBlobCache {
public:
MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
- const std::string& baseDir);
+ size_t maxTotalEntries, const std::string& baseDir);
~MultifileBlobCache();
void set(const void* key, EGLsizeiANDROID keySize, const void* value,
@@ -103,6 +114,13 @@
void finish();
size_t getTotalSize() const { return mTotalCacheSize; }
+ size_t getTotalEntries() const { return mTotalCacheEntries; }
+
+ const std::string& getCurrentBuildId() const { return mBuildId; }
+ void setCurrentBuildId(const std::string& buildId) { mBuildId = buildId; }
+
+ uint32_t getCurrentCacheVersion() const { return mCacheVersion; }
+ void setCurrentCacheVersion(uint32_t cacheVersion) { mCacheVersion = cacheVersion; }
private:
void trackEntry(uint32_t entryHash, EGLsizeiANDROID valueSize, size_t fileSize,
@@ -111,6 +129,9 @@
bool removeEntry(uint32_t entryHash);
MultifileEntryStats getEntryStats(uint32_t entryHash);
+ bool createStatus(const std::string& baseDir);
+ bool checkStatus(const std::string& baseDir);
+
size_t getFileSize(uint32_t entryHash);
size_t getValueSize(uint32_t entryHash);
@@ -120,12 +141,16 @@
bool addToHotCache(uint32_t entryHash, int fd, uint8_t* entryBufer, size_t entrySize);
bool removeFromHotCache(uint32_t entryHash);
+ bool clearCache();
void trimCache();
- bool applyLRU(size_t cacheLimit);
+ bool applyLRU(size_t cacheSizeLimit, size_t cacheEntryLimit);
bool mInitialized;
std::string mMultifileDirName;
+ std::string mBuildId;
+ uint32_t mCacheVersion;
+
std::unordered_set<uint32_t> mEntries;
std::unordered_map<uint32_t, MultifileEntryStats> mEntryStats;
std::unordered_map<uint32_t, MultifileHotCache> mHotCache;
@@ -133,7 +158,9 @@
size_t mMaxKeySize;
size_t mMaxValueSize;
size_t mMaxTotalSize;
+ size_t mMaxTotalEntries;
size_t mTotalCacheSize;
+ size_t mTotalCacheEntries;
size_t mHotCacheLimit;
size_t mHotCacheEntryLimit;
size_t mHotCacheSize;
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index 1639be6..90a0f1e 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -16,13 +16,17 @@
#include "MultifileBlobCache.h"
+#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <fcntl.h>
#include <gtest/gtest.h>
#include <stdio.h>
+#include <fstream>
#include <memory>
+using namespace std::literals;
+
namespace android {
template <typename T>
@@ -31,23 +35,40 @@
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
+constexpr size_t kMaxTotalEntries = 64;
class MultifileBlobCacheTest : public ::testing::Test {
protected:
virtual void SetUp() {
+ clearProperties();
mTempFile.reset(new TemporaryFile());
mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize,
- &mTempFile->path[0]));
+ kMaxTotalEntries, &mTempFile->path[0]));
}
- virtual void TearDown() { mMBC.reset(); }
+ virtual void TearDown() {
+ clearProperties();
+ mMBC.reset();
+ }
int getFileDescriptorCount();
+ std::vector<std::string> getCacheEntries();
+
+ void clearProperties();
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
};
+void MultifileBlobCacheTest::clearProperties() {
+ // Clear any debug properties used in the tests
+ base::SetProperty("debug.egl.blobcache.cache_version", "");
+ base::WaitForProperty("debug.egl.blobcache.cache_version", "");
+
+ base::SetProperty("debug.egl.blobcache.build_id", "");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "");
+}
+
TEST_F(MultifileBlobCacheTest, CacheSingleValueSucceeds) {
unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
mMBC->set("abcd", 4, "efgh", 4);
@@ -211,6 +232,23 @@
}
}
+TEST_F(MultifileBlobCacheTest, CacheMaxEntrySucceeds) {
+ // Fill the cache with max entries
+ int i = 0;
+ for (i = 0; i < kMaxTotalEntries; i++) {
+ mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
+ }
+
+ // Ensure it is full
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries);
+
+ // Add another entry
+ mMBC->set(std::to_string(i).c_str(), sizeof(i), std::to_string(i).c_str(), sizeof(i));
+
+ // Ensure total entries is cut in half + 1
+ ASSERT_EQ(mMBC->getTotalEntries(), kMaxTotalEntries / 2 + 1);
+}
+
TEST_F(MultifileBlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
unsigned char buf[1] = {0xee};
mMBC->set("x", 1, "y", 1);
@@ -234,8 +272,7 @@
TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
// Populate the cache with a bunch of entries
- size_t kLargeNumberOfEntries = 1024;
- for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ for (int i = 0; i < kMaxTotalEntries; i++) {
// printf("Caching: %i", i);
// Use the index as the key and value
@@ -247,27 +284,223 @@
}
// Ensure we don't have a bunch of open fds
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
// Close the cache so everything writes out
mMBC->finish();
mMBC.reset();
// Now open it again and ensure we still don't have a bunch of open fds
- mMBC.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
// Check after initialization
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
- for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ for (int i = 0; i < kMaxTotalEntries; i++) {
int result = 0;
ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
ASSERT_EQ(i, result);
}
// And again after we've actually used it
- ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+ ASSERT_LT(getFileDescriptorCount(), kMaxTotalEntries / 2);
+}
+
+std::vector<std::string> MultifileBlobCacheTest::getCacheEntries() {
+ std::string cachePath = &mTempFile->path[0];
+ std::string multifileDirName = cachePath + ".multifile";
+ std::vector<std::string> cacheEntries;
+
+ struct stat info;
+ if (stat(multifileDirName.c_str(), &info) == 0) {
+ // We have a multifile dir. Skip the status file and return the only entry.
+ DIR* dir;
+ struct dirent* entry;
+ if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
+ while ((entry = readdir(dir)) != nullptr) {
+ if (entry->d_name == "."s || entry->d_name == ".."s) {
+ continue;
+ }
+ if (strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
+ continue;
+ }
+ cacheEntries.push_back(multifileDirName + "/" + entry->d_name);
+ }
+ } else {
+ printf("Unable to open %s, error: %s\n", multifileDirName.c_str(),
+ std::strerror(errno));
+ }
+ } else {
+ printf("Unable to stat %s, error: %s\n", multifileDirName.c_str(), std::strerror(errno));
+ }
+
+ return cacheEntries;
+}
+
+TEST_F(MultifileBlobCacheTest, CacheContainsStatus) {
+ struct stat info;
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // After INIT, cache should have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure status lives after closing the cache
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+
+ // Open the cache again
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we still have a status
+ ASSERT_TRUE(stat(statusFile.str().c_str(), &info) == 0);
+}
+
+// Verify missing cache status file causes cache the be cleared
+TEST_F(MultifileBlobCacheTest, MissingCacheStatusClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Delete the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+ remove(statusFile.str().c_str());
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file BEGIN causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusBeginClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the beginning of the cache file
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(0, std::ios_base::beg);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify modified cache status file END causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, ModifiedCacheStatusEndClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Modify the status file
+ std::stringstream statusFile;
+ statusFile << &mTempFile->path[0] << ".multifile/" << kMultifileBlobCacheStatusFile;
+
+ // Stomp on the END of the cache status file, modifying its contents
+ const char* stomp = "BADF00D";
+ std::fstream fs(statusFile.str());
+ fs.seekp(-strlen(stomp), std::ios_base::end);
+ fs.write(stomp, strlen(stomp));
+ fs.flush();
+ fs.close();
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched cacheVersion causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedCacheVersionClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug cacheVersion
+ std::string newCacheVersion = std::to_string(kMultifileBlobCacheVersion + 1);
+ ASSERT_TRUE(base::SetProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+ ASSERT_TRUE(
+ base::WaitForProperty("debug.egl.blobcache.cache_version", newCacheVersion.c_str()));
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
+}
+
+// Verify mismatched buildId causes cache to be cleared
+TEST_F(MultifileBlobCacheTest, MismatchedBuildIdClears) {
+ // Set one entry
+ mMBC->set("abcd", 4, "efgh", 4);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Ensure there is one cache entry
+ ASSERT_EQ(getCacheEntries().size(), 1);
+
+ // Set a debug buildId
+ base::SetProperty("debug.egl.blobcache.build_id", "foo");
+ base::WaitForProperty("debug.egl.blobcache.build_id", "foo");
+
+ // Open the cache again and ensure no cache hits
+ mMBC.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &mTempFile->path[0]));
+
+ // Ensure we have no entries
+ ASSERT_EQ(getCacheEntries().size(), 0);
}
} // namespace android
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index 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_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index 1b68344..98ff1d1 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -41,6 +41,7 @@
constexpr uint32_t kMaxMultifileKeySize = 1 * 1024 * 1024;
constexpr uint32_t kMaxMultifileValueSize = 8 * 1024 * 1024;
constexpr uint32_t kMaxMultifileTotalSize = 32 * 1024 * 1024;
+constexpr uint32_t kMaxMultifileTotalEntries = 4 * 1024;
namespace android {
@@ -277,7 +278,7 @@
if (mMultifileBlobCache == nullptr) {
mMultifileBlobCache.reset(new MultifileBlobCache(kMaxMultifileKeySize,
kMaxMultifileValueSize, mCacheByteLimit,
- mFilename));
+ kMaxMultifileTotalEntries, mFilename));
}
return mMultifileBlobCache.get();
}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3317347..1ada33e 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -64,11 +64,6 @@
return false;
}
-bool needsAndroidPEglMitigation() {
- static const int32_t vndk_version = base::GetIntProperty("ro.vndk.version", -1);
- return vndk_version <= 28;
-}
-
int egl_get_init_count(EGLDisplay dpy) {
egl_display_t* eglDisplay = egl_display_t::get(dpy);
return eglDisplay ? eglDisplay->getRefsCount() : 0;
@@ -168,7 +163,7 @@
if (dpy == EGL_NO_DISPLAY) {
ALOGE("eglGetPlatformDisplay failed!");
} else {
- if (!angle::initializeAnglePlatform(dpy)) {
+ if (!angle::initializeAnglePlatform(dpy, cnx)) {
ALOGE("initializeAnglePlatform failed!");
}
}
@@ -365,14 +360,6 @@
if (len) {
// NOTE: we could avoid the copy if we had strnstr.
const std::string ext(start, len);
- // Mitigation for Android P vendor partitions: Adreno 530 driver shipped on
- // some Android P vendor partitions this extension under the draft KHR name,
- // but during Khronos review it was decided to demote it to EXT.
- if (needsAndroidPEglMitigation() && ext == "EGL_EXT_image_gl_colorspace" &&
- findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) {
- mExtensionString.append("EGL_EXT_image_gl_colorspace ");
- }
-
if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
mExtensionString.append(ext + " ");
}
@@ -433,7 +420,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/egl_display.h b/opengl/libs/EGL/egl_display.h
index 87c2176..867a117 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -39,7 +39,6 @@
struct egl_connection_t;
bool findExtension(const char* exts, const char* name, size_t nameLen = 0);
-bool needsAndroidPEglMitigation();
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 440eb17..a6af713 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -1644,26 +1644,6 @@
const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_NO_IMAGE_KHR;
- std::vector<AttrType> strippedAttribs;
- if (needsAndroidPEglMitigation()) {
- // Mitigation for Android P vendor partitions: eglImageCreateKHR should accept
- // EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and
- // EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported,
- // but some drivers don't like the DEFAULT value and generate an error.
- for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
- if (attr[0] == EGL_GL_COLORSPACE_KHR &&
- dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
- if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
- attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) {
- continue;
- }
- }
- strippedAttribs.push_back(attr[0]);
- strippedAttribs.push_back(attr[1]);
- }
- strippedAttribs.push_back(EGL_NONE);
- }
-
ContextRef _c(dp, ctx);
egl_context_t* const c = _c.get();
@@ -1671,8 +1651,7 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && eglCreateImageFunc) {
result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
- needsAndroidPEglMitigation() ? strippedAttribs.data()
- : attrib_list);
+ attrib_list);
}
return result;
}
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/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
index 633cc9c..2d9ee3a 100644
--- a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
+++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp
@@ -28,6 +28,7 @@
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
+constexpr size_t kMaxTotalEntries = 64;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// To fuzz this, we're going to create a key/value pair from data
@@ -79,8 +80,8 @@
std::unique_ptr<MultifileBlobCache> mbc;
tempFile.reset(new TemporaryFile());
- mbc.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
+ mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &tempFile->path[0]));
// With remaining data, select different paths below
int loopCount = 1;
uint8_t bumpCount = 0;
@@ -131,8 +132,8 @@
// Place the maxKey/maxValue twice
// The first will fit, the second will trigger hot cache trimming
tempFile.reset(new TemporaryFile());
- mbc.reset(
- new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
+ mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, kMaxTotalEntries,
+ &tempFile->path[0]));
uint8_t* buffer = new uint8_t[kMaxValueSize];
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
@@ -145,7 +146,7 @@
// overflow
tempFile.reset(new TemporaryFile());
mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize),
- &tempFile->path[0]));
+ kMaxTotalEntries, &tempFile->path[0]));
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
diff --git a/opengl/tests/EGLTest/egl_cache_test.cpp b/opengl/tests/EGLTest/egl_cache_test.cpp
index f81c68f..ce58182 100644
--- a/opengl/tests/EGLTest/egl_cache_test.cpp
+++ b/opengl/tests/EGLTest/egl_cache_test.cpp
@@ -114,25 +114,26 @@
struct stat info;
if (stat(multifileDirName.c_str(), &info) == 0) {
// Ensure we only have one file to manage
- int realFileCount = 0;
+ int entryFileCount = 0;
- // We have a multifile dir. Return the only real file in it.
+ // We have a multifile dir. Return the only entry file in it.
DIR* dir;
struct dirent* entry;
if ((dir = opendir(multifileDirName.c_str())) != nullptr) {
while ((entry = readdir(dir)) != nullptr) {
- if (entry->d_name == "."s || entry->d_name == ".."s) {
+ if (entry->d_name == "."s || entry->d_name == ".."s ||
+ strcmp(entry->d_name, kMultifileBlobCacheStatusFile) == 0) {
continue;
}
cachefileName = multifileDirName + "/" + entry->d_name;
- realFileCount++;
+ entryFileCount++;
}
} else {
printf("Unable to open %s, error: %s\n",
multifileDirName.c_str(), std::strerror(errno));
}
- if (realFileCount != 1) {
+ if (entryFileCount != 1) {
// If there was more than one real file in the directory, this
// violates test assumptions
cachefileName = "";
diff --git a/opengl/tests/gldual/AndroidManifest.xml b/opengl/tests/gldual/AndroidManifest.xml
index a36f4f7..d6335b0 100644
--- a/opengl/tests/gldual/AndroidManifest.xml
+++ b/opengl/tests/gldual/AndroidManifest.xml
@@ -20,8 +20,9 @@
android:label="@string/gldual_activity">
<activity android:name="GLDualActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:launchMode="singleTask"
- android:configChanges="orientation|keyboardHidden">
+ android:launchMode="singleTask"
+ android:configChanges="orientation|keyboardHidden"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index bf6189d..654c903 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -38,6 +38,7 @@
BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH
+ BATTERY_PROP_PART_STATUS = 12, // equals BATTERY_PROPERTY_PART_STATUS
};
struct BatteryProperties {
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..69f42bc 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -76,6 +76,8 @@
srcs: [
"InputCommonConverter.cpp",
"InputDeviceMetricsCollector.cpp",
+ "InputFilter.cpp",
+ "InputFilterCallbacks.cpp",
"InputProcessor.cpp",
"PointerChoreographer.cpp",
"PreferStylusOverTouchBlocker.cpp",
@@ -243,6 +245,9 @@
"Bug-115739809",
"StructLayout_test",
+ // jni
+ "libservices.core",
+
// rust targets
"libinput_rust_test",
@@ -256,6 +261,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/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index cefb140..b5cb3cb 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -125,58 +125,84 @@
void InputDeviceMetricsCollector::notifyInputDevicesChanged(
const NotifyInputDevicesChangedArgs& args) {
- reportCompletedSessions();
- onInputDevicesChanged(args.inputDeviceInfos);
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ onInputDevicesChanged(args.inputDeviceInfos);
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyConfigurationChanged(
const NotifyConfigurationChangedArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
- reportCompletedSessions();
- const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
- return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
- };
- onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
-
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
+ return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
+ };
+ onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyMotion(const NotifyMotionArgs& args) {
- reportCompletedSessions();
- onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
- [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime),
+ [&args](const auto&) { return getUsageSourcesForMotionArgs(args); });
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifySwitch(const NotifySwitchArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifySensor(const NotifySensorArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyVibratorState(const NotifyVibratorStateArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
void InputDeviceMetricsCollector::notifyPointerCaptureChanged(
const NotifyPointerCaptureChangedArgs& args) {
- reportCompletedSessions();
+ {
+ std::scoped_lock lock(mLock);
+ reportCompletedSessions();
+ }
mNextListener.notify(args);
}
@@ -185,10 +211,12 @@
if (isIgnoredInputDeviceId(deviceId)) {
return;
}
+ std::scoped_lock lock(mLock);
mInteractionsQueue.push(DeviceId{deviceId}, timestamp, uids);
}
void InputDeviceMetricsCollector::dump(std::string& dump) {
+ std::scoped_lock lock(mLock);
dump += "InputDeviceMetricsCollector:\n";
dump += " Logged device IDs: " + dumpMapKeys(mLoggedDeviceInfos, &toString) + "\n";
@@ -196,6 +224,10 @@
dumpMapKeys(mActiveUsageSessions, &toString) + "\n";
}
+void InputDeviceMetricsCollector::monitor() {
+ std::scoped_lock lock(mLock);
+}
+
void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 9633664..1bcd527 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -21,12 +21,14 @@
#include "NotifyArgs.h"
#include "SyncQueue.h"
+#include <android-base/thread_annotations.h>
#include <ftl/mixins.h>
#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
#include <chrono>
#include <functional>
#include <map>
+#include <mutex>
#include <set>
#include <vector>
@@ -34,8 +36,6 @@
/**
* Logs metrics about registered input devices and their usages.
- *
- * All methods in the InputListenerInterface must be called from a single thread.
*/
class InputDeviceMetricsCollectorInterface : public InputListenerInterface {
public:
@@ -50,6 +50,9 @@
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
+
+ /** Called by the heartbeat to ensure that this component has not deadlocked. */
+ virtual void monitor() = 0;
};
/** The logging interface for the metrics collector, injected for testing. */
@@ -116,10 +119,12 @@
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) override;
void dump(std::string& dump) override;
+ void monitor() override;
private:
+ std::mutex mLock;
InputListenerInterface& mNextListener;
- InputDeviceMetricsLogger& mLogger;
+ InputDeviceMetricsLogger& mLogger GUARDED_BY(mLock);
const std::chrono::nanoseconds mUsageSessionTimeout;
// Type-safe wrapper for input device id.
@@ -135,10 +140,10 @@
using Uid = gui::Uid;
using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo;
- std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos;
+ std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos GUARDED_BY(mLock);
using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
- SyncQueue<Interaction> mInteractionsQueue;
+ SyncQueue<Interaction> mInteractionsQueue GUARDED_BY(mLock);
class ActiveSession {
public:
@@ -166,16 +171,16 @@
};
// The input devices that currently have active usage sessions.
- std::map<DeviceId, ActiveSession> mActiveUsageSessions;
+ std::map<DeviceId, ActiveSession> mActiveUsageSessions GUARDED_BY(mLock);
- void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos);
- void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info);
+ void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) REQUIRES(mLock);
+ void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info) REQUIRES(mLock);
using SourceProvider =
std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>;
void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
- const SourceProvider& getSources);
- void onInputDeviceInteraction(const Interaction&);
- void reportCompletedSessions();
+ const SourceProvider& getSources) REQUIRES(mLock);
+ void onInputDeviceInteraction(const Interaction&) REQUIRES(mLock);
+ void reportCompletedSessions() REQUIRES(mLock);
};
} // namespace android
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
new file mode 100644
index 0000000..72c6f1a
--- /dev/null
+++ b/services/inputflinger/InputFilter.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputFilter"
+
+#include "InputFilter.h"
+
+namespace android {
+
+using aidl::com::android::server::inputflinger::IInputFilter;
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+using aidl::com::android::server::inputflinger::KeyEventAction;
+using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo;
+using aidl::android::hardware::input::common::Source;
+
+AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) {
+ AidlKeyEvent event;
+ event.id = args.id;
+ event.eventTime = args.eventTime;
+ event.deviceId = args.deviceId;
+ event.source = static_cast<Source>(args.source);
+ event.displayId = args.displayId;
+ event.policyFlags = args.policyFlags;
+ event.action = static_cast<KeyEventAction>(args.action);
+ event.flags = args.flags;
+ event.keyCode = args.keyCode;
+ event.scanCode = args.scanCode;
+ event.metaState = args.metaState;
+ event.downTime = args.downTime;
+ event.readTime = args.readTime;
+ return event;
+}
+
+InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust,
+ InputFilterPolicyInterface& policy)
+ : mNextListener(listener),
+ mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener, policy)),
+ mPolicy(policy) {
+ LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk());
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust);
+}
+
+void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ mDeviceInfos.clear();
+ mDeviceInfos.reserve(args.inputDeviceInfos.size());
+ for (auto info : args.inputDeviceInfos) {
+ AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back();
+ aidlInfo.deviceId = info.getId();
+ aidlInfo.external = info.isExternal();
+ }
+ if (isFilterEnabled()) {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk());
+ }
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyKey(const NotifyKeyArgs& args) {
+ if (isFilterEnabled()) {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
+ return;
+ }
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifySwitch(const NotifySwitchArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifySensor(const NotifySensorArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ mNextListener.notify(args);
+}
+
+void InputFilter::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+bool InputFilter::isFilterEnabled() {
+ bool result;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->isEnabled(&result).isOk());
+ return result;
+}
+
+void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) {
+ std::scoped_lock _l(mLock);
+
+ if (mConfig.bounceKeysThresholdNs != threshold) {
+ mConfig.bounceKeysThresholdNs = threshold;
+ notifyConfigurationChangedLocked();
+ }
+}
+
+void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+
+ if (mConfig.stickyKeysEnabled != enabled) {
+ mConfig.stickyKeysEnabled = enabled;
+ notifyConfigurationChangedLocked();
+ if (!enabled) {
+ // When Sticky keys is disabled, send callback to clear any saved sticky state.
+ mPolicy.notifyStickyModifierStateChanged(0, 0);
+ }
+ }
+}
+
+void InputFilter::notifyConfigurationChangedLocked() {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ if (isFilterEnabled()) {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk());
+ }
+}
+
+void InputFilter::dump(std::string& dump) {
+ dump += "InputFilter:\n";
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
new file mode 100644
index 0000000..153d29d
--- /dev/null
+++ b/services/inputflinger/InputFilter.h
@@ -0,0 +1,82 @@
+/*
+ * 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 <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <utils/Mutex.h>
+#include "InputFilterCallbacks.h"
+#include "InputFilterPolicyInterface.h"
+#include "InputListener.h"
+#include "NotifyArgs.h"
+
+namespace android {
+
+/**
+ * The C++ component of InputFilter designed as a wrapper around the rust implementation.
+ */
+class InputFilterInterface : public InputListenerInterface {
+public:
+ /**
+ * This method may be called on any thread (usually by the input manager on a binder thread).
+ */
+ virtual void dump(std::string& dump) = 0;
+ virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
+ virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0;
+};
+
+class InputFilter : public InputFilterInterface {
+public:
+ using IInputFlingerRust = aidl::com::android::server::inputflinger::IInputFlingerRust;
+ using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
+ using IInputFilterCallbacks =
+ aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks;
+ using InputFilterConfiguration =
+ aidl::com::android::server::inputflinger::InputFilterConfiguration;
+ using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo;
+
+ explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust,
+ InputFilterPolicyInterface& policy);
+ ~InputFilter() override = default;
+ void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+ void notifyKey(const NotifyKeyArgs& args) override;
+ void notifyMotion(const NotifyMotionArgs& args) override;
+ void notifySwitch(const NotifySwitchArgs& args) override;
+ void notifySensor(const NotifySensorArgs& args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override;
+ void setAccessibilityStickyKeysEnabled(bool enabled) override;
+ void dump(std::string& dump) override;
+
+private:
+ InputListenerInterface& mNextListener;
+ std::shared_ptr<InputFilterCallbacks> mCallbacks;
+ InputFilterPolicyInterface& mPolicy;
+ std::shared_ptr<IInputFilter> mInputFilterRust;
+ // Keep track of connected peripherals, so that if filters are enabled later, we can pass that
+ // info to the filters
+ std::vector<AidlDeviceInfo> mDeviceInfos;
+ mutable std::mutex mLock;
+ InputFilterConfiguration mConfig GUARDED_BY(mLock);
+
+ bool isFilterEnabled();
+ void notifyConfigurationChangedLocked() REQUIRES(mLock);
+};
+
+} // namespace android
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
new file mode 100644
index 0000000..a8759b7
--- /dev/null
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputFilterCallbacks"
+
+#include "InputFilterCallbacks.h"
+
+namespace android {
+
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
+ return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
+ static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
+ static_cast<int32_t>(event.action), event.flags, event.keyCode,
+ event.scanCode, event.metaState, event.downTime);
+}
+
+InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener,
+ InputFilterPolicyInterface& policy)
+ : mNextListener(listener), mPolicy(policy) {}
+
+ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) {
+ mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState,
+ int32_t lockedModifierState) {
+ std::scoped_lock _l(mLock);
+ mStickyModifierState.modifierState = modifierState;
+ mStickyModifierState.lockedModifierState = lockedModifierState;
+ mPolicy.notifyStickyModifierStateChanged(modifierState, lockedModifierState);
+ ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d",
+ modifierState, lockedModifierState);
+ return ndk::ScopedAStatus::ok();
+}
+
+uint32_t InputFilterCallbacks::getModifierState() {
+ std::scoped_lock _l(mLock);
+ return mStickyModifierState.modifierState;
+}
+
+uint32_t InputFilterCallbacks::getLockedModifierState() {
+ std::scoped_lock _l(mLock);
+ return mStickyModifierState.lockedModifierState;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h
new file mode 100644
index 0000000..31c160a
--- /dev/null
+++ b/services/inputflinger/InputFilterCallbacks.h
@@ -0,0 +1,58 @@
+/*
+ * 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 <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <android/binder_auto_utils.h>
+#include <utils/Mutex.h>
+#include <mutex>
+#include "InputFilterPolicyInterface.h"
+#include "InputListener.h"
+#include "NotifyArgs.h"
+
+/**
+ * The C++ component of InputFilter designed as a wrapper around the rust callback implementation.
+ */
+namespace android {
+
+using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks {
+public:
+ explicit InputFilterCallbacks(InputListenerInterface& listener,
+ InputFilterPolicyInterface& policy);
+ ~InputFilterCallbacks() override = default;
+
+ uint32_t getModifierState();
+ uint32_t getLockedModifierState();
+
+private:
+ InputListenerInterface& mNextListener;
+ InputFilterPolicyInterface& mPolicy;
+ mutable std::mutex mLock;
+ struct StickyModifierState {
+ uint32_t modifierState;
+ uint32_t lockedModifierState;
+ } mStickyModifierState GUARDED_BY(mLock);
+
+ ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override;
+ ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState,
+ int32_t lockedModifierState) override;
+};
+
+} // namespace android
\ No newline at end of file
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/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 92c65e1..4863513 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -42,6 +42,7 @@
sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
int32_t exceptionCodeFromStatusT(status_t status) {
switch (status) {
@@ -118,6 +119,7 @@
* The event flow is via the "InputListener" interface, as follows:
* InputReader
* -> UnwantedInteractionBlocker
+ * -> InputFilter
* -> PointerChoreographer
* -> InputProcessor
* -> InputDeviceMetricsCollector
@@ -125,13 +127,21 @@
*/
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
InputDispatcherPolicyInterface& dispatcherPolicy,
- PointerChoreographerPolicyInterface& choreographerPolicy) {
+ PointerChoreographerPolicyInterface& choreographerPolicy,
+ InputFilterPolicyInterface& inputFilterPolicy) {
mInputFlingerRust = createInputFlingerRust();
mDispatcher = createInputDispatcher(dispatcherPolicy);
mTracingStages.emplace_back(
std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
+ if (ENABLE_INPUT_FILTER_RUST) {
+ mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust,
+ inputFilterPolicy);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
+ }
+
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
mTracingStages.emplace_back(
@@ -216,10 +226,17 @@
return *mDispatcher;
}
+InputFilterInterface& InputManager::getInputFilter() {
+ return *mInputFilter;
+}
+
void InputManager::monitor() {
mReader->monitor();
mBlocker->monitor();
mProcessor->monitor();
+ if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
+ mCollector->monitor();
+ }
mDispatcher->monitor();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 20b9fd5..df944ef 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -21,6 +21,7 @@
*/
#include "InputDeviceMetricsCollector.h"
+#include "InputFilter.h"
#include "InputProcessor.h"
#include "InputReaderBase.h"
#include "PointerChoreographer.h"
@@ -28,6 +29,7 @@
#include <InputDispatcherInterface.h>
#include <InputDispatcherPolicyInterface.h>
+#include <InputFilterPolicyInterface.h>
#include <PointerChoreographerPolicyInterface.h>
#include <input/Input.h>
#include <input/InputTransport.h>
@@ -40,6 +42,7 @@
using android::os::BnInputFlinger;
+using aidl::com::android::server::inputflinger::IInputFilter;
using aidl::com::android::server::inputflinger::IInputFlingerRust;
namespace android {
@@ -100,6 +103,9 @@
/* Gets the input dispatcher. */
virtual InputDispatcherInterface& getDispatcher() = 0;
+ /* Gets the input filter */
+ virtual InputFilterInterface& getInputFilter() = 0;
+
/* Check that the input stages have not deadlocked. */
virtual void monitor() = 0;
@@ -114,7 +120,8 @@
public:
InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
InputDispatcherPolicyInterface& dispatcherPolicy,
- PointerChoreographerPolicyInterface& choreographerPolicy);
+ PointerChoreographerPolicyInterface& choreographerPolicy,
+ InputFilterPolicyInterface& inputFilterPolicy);
status_t start() override;
status_t stop() override;
@@ -124,6 +131,7 @@
InputProcessorInterface& getProcessor() override;
InputDeviceMetricsCollectorInterface& getMetricsCollector() override;
InputDispatcherInterface& getDispatcher() override;
+ InputFilterInterface& getInputFilter() override;
void monitor() override;
void dump(std::string& dump) override;
@@ -137,6 +145,8 @@
std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker;
+ std::unique_ptr<InputFilterInterface> mInputFilter;
+
std::unique_ptr<PointerChoreographerInterface> mChoreographer;
std::unique_ptr<InputProcessorInterface> mProcessor;
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..0be4c32 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -16,17 +16,56 @@
#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 isFromTouchpad(const NotifyMotionArgs& args) {
+ return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
+ args.pointerProperties[0].toolType == ToolType::FINGER;
+}
+
+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 +78,153 @@
}
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 (isFromTouchpad(args)) {
+ return processTouchpadEventLocked(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();
+ }
+
+ auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+
+ 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;
+}
+
+NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
+ auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+
+ NotifyMotionArgs newArgs(args);
+ newArgs.displayId = displayId;
+ if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
+ // This is a movement of the mouse pointer.
+ 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();
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+ } else {
+ // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
+ pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ const auto [x, y] = pc.getPosition();
+ for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
+ newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
+ args.pointerCoords[i].getX() + x);
+ newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ args.pointerCoords[i].getY() + y);
+ }
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+ }
+ 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 +240,304 @@
}
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;
+}
+
+std::pair<int32_t, PointerControllerInterface&>
+PointerChoreographer::getDisplayIdAndMouseControllerLocked(int32_t associatedDisplayId) {
+ const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
+
+ // 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();
+ }
+
+ return {displayId, *it->second};
+}
+
+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, and create
+ // new PointerControllers if necessary.
+ 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 displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
+ mouseDisplaysToKeep.insert(displayId);
+ // For mice, show the cursor immediately when the device is first connected or
+ // when it moves to a new display.
+ auto [mousePointerIt, isNewMousePointer] =
+ mMousePointersByDisplay.try_emplace(displayId,
+ getMouseControllerConstructor(displayId));
+ auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId());
+ if (isNewMouseDevice || isNewMousePointer) {
+ mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+ }
+ 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();
+ });
+ std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
+ return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
+ [id](const auto& info) { return info.getId() == id; }) ==
+ mInputDeviceInfos.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();
+}
+
+bool PointerChoreographer::setPointerIcon(
+ std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId,
+ DeviceId deviceId) {
+ std::scoped_lock _l(mLock);
+ if (deviceId < 0) {
+ LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
+ return false;
+ }
+ const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+ if (!info) {
+ LOG(WARNING) << "No input device info found for id " << deviceId
+ << ". Cannot set pointer icon.";
+ return false;
+ }
+ const uint32_t sources = info->getSources();
+ const auto stylusPointerIt = mStylusPointersByDevice.find(deviceId);
+
+ if (isFromSource(sources, AINPUT_SOURCE_STYLUS) &&
+ stylusPointerIt != mStylusPointersByDevice.end()) {
+ if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
+ if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
+ LOG(FATAL) << "SpriteIcon should not be null";
+ }
+ stylusPointerIt->second->setCustomPointerIcon(
+ *std::get<std::unique_ptr<SpriteIcon>>(icon));
+ } else {
+ stylusPointerIt->second->updatePointerIcon(std::get<PointerIconStyle>(icon));
+ }
+ } else if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
+ if (const auto mousePointerIt = mMousePointersByDisplay.find(displayId);
+ mousePointerIt != mMousePointersByDisplay.end()) {
+ if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
+ if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
+ LOG(FATAL) << "SpriteIcon should not be null";
+ }
+ mousePointerIt->second->setCustomPointerIcon(
+ *std::get<std::unique_ptr<SpriteIcon>>(icon));
+ } else {
+ mousePointerIt->second->updatePointerIcon(std::get<PointerIconStyle>(icon));
+ }
+ } else {
+ LOG(WARNING) << "No mouse pointer controller found for display " << displayId
+ << ", device " << deviceId << ".";
+ return false;
+ }
+ } else {
+ LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device "
+ << deviceId << ".";
+ return false;
+ }
+ return true;
+}
+
+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..f46419e 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -20,8 +20,27 @@
#include "NotifyArgs.h"
#include "PointerChoreographerPolicyInterface.h"
+#include <android-base/thread_annotations.h>
+#include <type_traits>
+
namespace android {
+struct SpriteIcon;
+
+/**
+ * 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.
@@ -31,6 +50,25 @@
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;
+ /**
+ * Set the icon that is shown for the given pointer. The request may fail in some cases, such
+ * as if the device or display was removed, or if the cursor was moved to a different display.
+ * Returns true if the icon was changed successfully, false otherwise.
+ */
+ virtual bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
+ int32_t displayId, DeviceId deviceId) = 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 +80,16 @@
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;
+ bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
+ int32_t displayId, DeviceId deviceId) override;
+
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
@@ -55,7 +103,46 @@
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);
+ std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked(
+ int32_t associatedDisplayId) REQUIRES(mLock);
+ InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
+
+ NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
+ NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+ NotifyMotionArgs processTouchpadEventLocked(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::set<DeviceId> mMouseDevices 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/aidl/Android.bp b/services/inputflinger/aidl/Android.bp
index 314c433..d068129 100644
--- a/services/inputflinger/aidl/Android.bp
+++ b/services/inputflinger/aidl/Android.bp
@@ -17,6 +17,9 @@
srcs: ["**/*.aidl"],
unstable: true,
host_supported: true,
+ imports: [
+ "android.hardware.input.common-V1",
+ ],
backend: {
cpp: {
enabled: false,
diff --git a/libs/gui/include/gui/Flags.h b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
similarity index 73%
copy from libs/gui/include/gui/Flags.h
copy to services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
index a2cff56..b9e6a03 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-#pragma once
+package com.android.server.inputflinger;
-// 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
+/**
+ * Analogous to Android's InputDeviceInfo
+ * Stores the basic information connected input devices.
+ */
+parcelable DeviceInfo {
+ int deviceId;
+ boolean external;
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
new file mode 100644
index 0000000..2921d30
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputflinger;
+
+import com.android.server.inputflinger.DeviceInfo;
+import com.android.server.inputflinger.InputFilterConfiguration;
+import com.android.server.inputflinger.KeyEvent;
+
+/**
+ * A local AIDL interface used as a foreign function interface (ffi) to
+ * filter input events.
+ *
+ * NOTE: Since we use this as a local interface, all processing happens on the
+ * calling thread.
+ */
+interface IInputFilter {
+
+ /** Callbacks for the rust InputFilter to call into C++. */
+ interface IInputFilterCallbacks {
+ /** Sends back a filtered key event */
+ void sendKeyEvent(in KeyEvent event);
+
+ /** Sends back modifier state */
+ void onModifierStateChanged(int modifierState, int lockedModifierState);
+ }
+
+ /** Returns if InputFilter is enabled */
+ boolean isEnabled();
+
+ /** Notifies if a key event occurred */
+ void notifyKey(in KeyEvent event);
+
+ /** Notifies if any InputDevice list changed and provides the list of connected peripherals */
+ void notifyInputDevicesChanged(in DeviceInfo[] deviceInfos);
+
+ /** Notifies when configuration changes */
+ void notifyConfigurationChanged(in InputFilterConfiguration config);
+}
+
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
index 8e826fd..de852c0 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
@@ -16,6 +16,9 @@
package com.android.server.inputflinger;
+import com.android.server.inputflinger.IInputFilter;
+import com.android.server.inputflinger.IInputFilter.IInputFilterCallbacks;
+
/**
* A local AIDL interface used as a foreign function interface (ffi) to
* communicate with the Rust component of inputflinger.
@@ -31,4 +34,7 @@
interface IInputFlingerRustBootstrapCallback {
void onProvideInputFlingerRust(in IInputFlingerRust inputFlingerRust);
}
+
+ /** Create the rust implementation of InputFilter. */
+ IInputFilter createInputFilter(IInputFilterCallbacks callbacks);
}
diff --git a/libs/gui/include/gui/Flags.h b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
similarity index 64%
copy from libs/gui/include/gui/Flags.h
copy to services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
index a2cff56..38b1612 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
@@ -14,9 +14,14 @@
* limitations under the License.
*/
-#pragma once
+package com.android.server.inputflinger;
-// 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
+/**
+ * Contains data for the current Input filter configuration
+ */
+parcelable InputFilterConfiguration {
+ // Threshold value for Bounce keys filter (check bounce_keys_filter.rs)
+ long bounceKeysThresholdNs;
+ // If sticky keys filter is enabled
+ boolean stickyKeysEnabled;
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
new file mode 100644
index 0000000..2cae6e1
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEvent.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputflinger;
+
+import android.hardware.input.common.Source;
+import com.android.server.inputflinger.KeyEventAction;
+
+/**
+ * Analogous to Android's native KeyEvent / NotifyKeyArgs.
+ * Stores the basic information about Key events.
+ */
+@RustDerive(Copy=true, Clone=true, Eq=true, PartialEq=true)
+parcelable KeyEvent {
+ int id;
+ int deviceId;
+ long downTime;
+ long readTime;
+ long eventTime;
+ Source source;
+ int displayId;
+ int policyFlags;
+ KeyEventAction action;
+ int flags;
+ int keyCode;
+ int scanCode;
+ int metaState;
+}
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl
new file mode 100644
index 0000000..43ee5fe
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/KeyEventAction.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputflinger;
+
+/** Different Key event actions */
+enum KeyEventAction {
+ /** The key has been pressed down. */
+ DOWN = 0,
+
+ /** The key has been released. */
+ UP = 1,
+
+ /**
+ * Multiple duplicate key events have occurred in a row, or a
+ * complex string is being delivered. The repeat_count property
+ * of the key event contains the number of times the given key
+ * code should be executed.
+ *
+ * NOTE: This is deprecated and should never be used. This just
+ * for consistency with KeyEvent actions defined in NotifyKeyArgs.
+ */
+ MULTIPLE = 2
+}
\ No newline at end of file
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..cc0d49c 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,34 +124,32 @@
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),
displayId(displayId),
action(action),
- flags(flags),
keyCode(keyCode),
scanCode(scanCode),
metaState(metaState),
- repeatCount(repeatCount),
downTime(downTime),
syntheticRepeat(false),
interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
- interceptKeyWakeupTime(0) {}
-
-KeyEntry::~KeyEntry() {}
+ interceptKeyWakeupTime(0),
+ flags(flags),
+ repeatCount(repeatCount) {
+ 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, "
@@ -310,7 +276,7 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
+DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags,
const ui::Transform& transform, const ui::Transform& rawTransform,
float globalScaleFactor)
@@ -321,21 +287,15 @@
rawTransform(rawTransform),
globalScaleFactor(globalScaleFactor),
deliveryTime(0),
- resolvedAction(0),
resolvedFlags(0) {
switch (this->eventEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<KeyEntry&>(*this->eventEntry);
- resolvedEventId = keyEntry.id;
- resolvedAction = keyEntry.action;
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*this->eventEntry);
resolvedFlags = keyEntry.flags;
-
break;
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<MotionEntry&>(*this->eventEntry);
- resolvedEventId = motionEntry.id;
- resolvedAction = motionEntry.action;
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*this->eventEntry);
resolvedFlags = motionEntry.flags;
break;
}
@@ -355,24 +315,9 @@
}
std::ostream& operator<<(std::ostream& out, const DispatchEntry& entry) {
- out << "DispatchEntry{resolvedAction=";
- switch (entry.eventEntry->type) {
- case EventEntry::Type::KEY: {
- out << KeyEvent::actionToString(entry.resolvedAction);
- break;
- }
- case EventEntry::Type::MOTION: {
- out << MotionEvent::actionToString(entry.resolvedAction);
- break;
- }
- default: {
- out << "<invalid, not a key or a motion>";
- break;
- }
- }
std::string transform;
entry.transform.dump(transform, "transform");
- out << ", resolvedFlags=" << entry.resolvedFlags
+ out << "DispatchEntry{resolvedFlags=" << entry.resolvedFlags
<< ", targetFlags=" << entry.targetFlags.string() << ", transform=" << transform
<< "} original: " << entry.eventEntry->getDescription();
return out;
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index b341784..e2e13c3 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -48,9 +48,9 @@
Type type;
nsecs_t eventTime;
uint32_t policyFlags;
- InjectionState* injectionState;
+ std::shared_ptr<InjectionState> injectionState;
- bool dispatchInProgress; // initially false, set to true while dispatching
+ mutable bool dispatchInProgress; // initially false, set to true while dispatching
/**
* Injected keys are events from an external (probably untrusted) application
@@ -72,17 +72,14 @@
virtual std::string getDescription() const = 0;
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
- virtual ~EventEntry();
-
-protected:
- void releaseInjectionState();
+ EventEntry(const EventEntry&) = delete;
+ EventEntry& operator=(const EventEntry&) = delete;
+ 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 +87,6 @@
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
std::string getDescription() const override;
-
- ~DeviceResetEntry() override;
};
struct FocusEntry : EventEntry {
@@ -102,8 +97,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 +104,6 @@
PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
std::string getDescription() const override;
-
- ~PointerCaptureChangedEntry() override;
};
struct DragEntry : EventEntry {
@@ -123,8 +114,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 {
@@ -132,11 +121,9 @@
uint32_t source;
int32_t displayId;
int32_t action;
- int32_t flags;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
- int32_t repeatCount;
nsecs_t downTime;
bool syntheticRepeat; // set to true for synthetic key repeats
@@ -147,16 +134,17 @@
CONTINUE,
TRY_AGAIN_LATER,
};
- InterceptKeyResult interceptKeyResult; // set based on the interception result
- nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
+ // These are special fields that may need to be modified while the event is being dispatched.
+ mutable InterceptKeyResult interceptKeyResult; // set based on the interception result
+ mutable nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
+ mutable int32_t flags;
+ mutable int32_t repeatCount;
- 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 +168,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 +195,6 @@
InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
std::vector<float> values);
std::string getDescription() const override;
-
- ~SensorEntry() override;
};
struct TouchModeEntry : EventEntry {
@@ -219,15 +203,13 @@
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.
struct DispatchEntry {
const uint32_t seq; // unique sequence number, never 0
- std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
+ std::shared_ptr<const EventEntry> eventEntry; // the event to dispatch
const ftl::Flags<InputTarget::Flags> targetFlags;
ui::Transform transform;
ui::Transform rawTransform;
@@ -238,12 +220,9 @@
// An ANR will be triggered if a response for this entry is not received by timeoutTime
nsecs_t timeoutTime;
- // Set to the resolved ID, action and flags when the event is enqueued.
- int32_t resolvedEventId;
- int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
+ DispatchEntry(std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform,
const ui::Transform& rawTransform, float globalScaleFactor);
DispatchEntry(const DispatchEntry&) = delete;
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..162a7bd 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -72,6 +72,9 @@
using android::os::InputEventInjectionSync;
namespace input_flags = com::android::input::flags;
+// TODO(b/312714754): remove the corresponding code, as well.
+static const bool REMOVE_APP_SWITCH_DROPS = true;
+
namespace android::inputdispatcher {
namespace {
@@ -124,10 +127,6 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-bool isEmpty(const std::stringstream& ss) {
- return ss.rdbuf()->in_avail() == 0;
-}
-
inline const std::string binderToString(const sp<IBinder>& binder) {
if (binder == nullptr) {
return "<null>";
@@ -254,6 +253,14 @@
}
}
+std::bitset<MAX_POINTER_ID + 1> getPointerIds(const std::vector<PointerProperties>& pointers) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ for (const PointerProperties& pointer : pointers) {
+ pointerIds.set(pointer.id);
+ }
+ return pointerIds;
+}
+
std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
return "<empty>";
@@ -293,9 +300,8 @@
}
dump.append(INDENT4);
dump += entry.eventEntry->getDescription();
- dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, resolvedAction=%d, age=%" PRId64
- "ms",
- entry.seq, entry.targetFlags.string().c_str(), entry.resolvedAction,
+ dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, age=%" PRId64 "ms", entry.seq,
+ entry.targetFlags.string().c_str(),
ns2ms(currentTime - entry.eventEntry->eventTime));
if (entry.deliveryTime != 0) {
// This entry was delivered, so add information on how long we've been waiting
@@ -352,7 +358,7 @@
}
std::unique_ptr<DispatchEntry> createDispatchEntry(
- const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry,
+ const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> inputTargetFlags) {
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
@@ -390,21 +396,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,
@@ -457,10 +459,6 @@
bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) {
const EventEntry& eventEntry = *dispatchEntry.eventEntry;
const int32_t& inputEventId = eventEntry.id;
- if (inputEventId != dispatchEntry.resolvedEventId) {
- // Event was transmuted
- return false;
- }
if (inputEventId == android::os::IInputConstants::INVALID_INPUT_EVENT_ID) {
return false;
}
@@ -641,22 +639,22 @@
}
// We should consider all hovering pointers here. But for now, just use the first one
- const int32_t pointerId = entry.pointerProperties[0].id;
+ const PointerProperties& pointer = entry.pointerProperties[0];
std::set<sp<WindowInfoHandle>> oldWindows;
if (oldState != nullptr) {
- oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId);
+ oldWindows = oldState->getWindowsWithHoveringPointer(entry.deviceId, pointer.id);
}
std::set<sp<WindowInfoHandle>> newWindows =
- newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointerId);
+ newTouchState.getWindowsWithHoveringPointer(entry.deviceId, pointer.id);
// If the pointer is no longer in the new window set, send HOVER_EXIT.
for (const sp<WindowInfoHandle>& oldWindow : oldWindows) {
if (newWindows.find(oldWindow) == newWindows.end()) {
TouchedWindow touchedWindow;
touchedWindow.windowHandle = oldWindow;
- touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_EXIT;
+ touchedWindow.dispatchMode = InputTarget::DispatchMode::HOVER_EXIT;
out.push_back(touchedWindow);
}
}
@@ -667,7 +665,7 @@
if (oldWindows.find(newWindow) == oldWindows.end()) {
// Any windows that have this pointer now, and didn't have it before, should get
// HOVER_ENTER
- touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_HOVER_ENTER;
+ touchedWindow.dispatchMode = InputTarget::DispatchMode::HOVER_ENTER;
} else {
// This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) {
@@ -681,9 +679,9 @@
}
LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
}
- touchedWindow.targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
+ touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
}
- touchedWindow.addHoveringPointer(entry.deviceId, pointerId);
+ touchedWindow.addHoveringPointer(entry.deviceId, pointer);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -751,15 +749,25 @@
return true;
}
+/**
+ * Return true if stylus is currently down anywhere on the specified display, and false otherwise.
+ */
+bool isStylusActiveInDisplay(
+ int32_t displayId,
+ const std::unordered_map<int32_t /*displayId*/, TouchState>& touchStatesByDisplay) {
+ const auto it = touchStatesByDisplay.find(displayId);
+ if (it == touchStatesByDisplay.end()) {
+ return false;
+ }
+ const TouchState& state = it->second;
+ return state.hasActiveStylus();
+}
+
} // namespace
// --- 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 +782,6 @@
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
- mStaleEventTimeout(staleEventTimeout),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator) {
mLooper = sp<Looper>::make(false);
@@ -828,7 +835,7 @@
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
- dispatchOnceInnerLocked(&nextWakeupTime);
+ dispatchOnceInnerLocked(/*byref*/ nextWakeupTime);
}
// Run all pending commands if there are any.
@@ -935,7 +942,7 @@
return DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}
-void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) {
nsecs_t currentTime = now();
// Reset the key repeat timer whenever normal dispatch is suspended while the
@@ -956,20 +963,23 @@
// 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;
+ nextWakeupTime = std::min(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.
@@ -977,9 +987,7 @@
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
} else {
- if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
- *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
- }
+ nextWakeupTime = std::min(nextWakeupTime, mKeyRepeatState.nextRepeatTime);
}
}
@@ -1033,8 +1041,8 @@
}
case EventEntry::Type::FOCUS: {
- std::shared_ptr<FocusEntry> typedEntry =
- std::static_pointer_cast<FocusEntry>(mPendingEvent);
+ std::shared_ptr<const FocusEntry> typedEntry =
+ std::static_pointer_cast<const FocusEntry>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
@@ -1042,7 +1050,7 @@
}
case EventEntry::Type::TOUCH_MODE_CHANGED: {
- const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent);
+ const auto typedEntry = std::static_pointer_cast<const TouchModeEntry>(mPendingEvent);
dispatchTouchModeChangeLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped
@@ -1051,28 +1059,31 @@
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
const auto typedEntry =
- std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
+ std::static_pointer_cast<const PointerCaptureChangedEntry>(mPendingEvent);
dispatchPointerCaptureChangedLocked(currentTime, typedEntry, dropReason);
done = true;
break;
}
case EventEntry::Type::DRAG: {
- std::shared_ptr<DragEntry> typedEntry =
- std::static_pointer_cast<DragEntry>(mPendingEvent);
+ std::shared_ptr<const DragEntry> typedEntry =
+ std::static_pointer_cast<const DragEntry>(mPendingEvent);
dispatchDragLocked(currentTime, typedEntry);
done = true;
break;
}
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;
+ std::shared_ptr<const KeyEntry> keyEntry =
+ std::static_pointer_cast<const KeyEntry>(mPendingEvent);
+ 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)) {
@@ -1086,26 +1097,42 @@
}
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;
+ std::shared_ptr<const MotionEntry> motionEntry =
+ std::static_pointer_cast<const MotionEntry>(mPendingEvent);
+ 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;
+ // The event is stale. However, only drop stale events if there isn't an ongoing
+ // gesture. That would allow us to complete the processing of the current stroke.
+ const auto touchStateIt = mTouchStatesByDisplay.find(motionEntry->displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end()) {
+ const TouchState& touchState = touchStateIt->second;
+ if (!touchState.hasTouchingPointers(motionEntry->deviceId) &&
+ !touchState.hasHoveringPointers(motionEntry->deviceId)) {
+ dropReason = DropReason::STALE;
+ }
+ }
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
- dropReason = DropReason::BLOCKED;
+ if (!isFromSource(motionEntry->source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // Only drop events that are focus-dispatched.
+ dropReason = DropReason::BLOCKED;
+ }
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
break;
}
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;
+ std::shared_ptr<const SensorEntry> sensorEntry =
+ std::static_pointer_cast<const SensorEntry>(mPendingEvent);
+ 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.
@@ -1126,12 +1153,12 @@
mLastDropReason = dropReason;
releasePendingEventLocked();
- *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
+ nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
}
}
bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
- return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout;
+ return mPolicy.isStaleEvent(currentTime, entry.eventTime);
}
/**
@@ -1196,7 +1223,7 @@
bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
bool needWake = mInboundQueue.empty();
mInboundQueue.push_back(std::move(newEntry));
- EventEntry& entry = *(mInboundQueue.back());
+ const EventEntry& entry = *(mInboundQueue.back());
traceInboundQueueLengthLocked();
switch (entry.type) {
@@ -1207,27 +1234,29 @@
// 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.
if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent &&
mPendingEvent->type == EventEntry::Type::KEY) {
- KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
+ const KeyEntry& pendingKey = static_cast<const KeyEntry&>(*mPendingEvent);
if (pendingKey.keyCode == keyEntry.keyCode &&
pendingKey.interceptKeyResult ==
KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
@@ -1242,7 +1271,7 @@
case EventEntry::Type::MOTION: {
LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
"Unexpected untrusted event.");
- if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
+ if (shouldPruneInboundQueueLocked(static_cast<const MotionEntry&>(entry))) {
mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
@@ -1266,7 +1295,7 @@
return needWake;
}
-void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) {
+void InputDispatcher::addRecentEventLocked(std::shared_ptr<const EventEntry> entry) {
// Do not store sensor event in recent queue to avoid flooding the queue.
if (entry->type != EventEntry::Type::SENSOR) {
mRecentQueue.push_back(entry);
@@ -1314,8 +1343,8 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- addPointerWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
- pointerIds,
+ addPointerWindowTargetLocked(windowHandle, InputTarget::DispatchMode::OUTSIDE,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
/*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
}
}
@@ -1362,8 +1391,9 @@
reason = "inbound event was dropped because of pending overdue app switch";
break;
case DropReason::BLOCKED:
- ALOGI("Dropped event because the current application is not responding and the user "
- "has started interacting with a different application.");
+ LOG(INFO) << "Dropping because the current application is not responding and the user "
+ "has started interacting with a different application: "
+ << entry.getDescription();
reason = "inbound event was dropped because the current application is not responding "
"and the user has started interacting with a different application";
break;
@@ -1384,6 +1414,9 @@
switch (entry.type) {
case EventEntry::Type::KEY: {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ options.displayId = keyEntry.displayId;
+ options.deviceId = keyEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
@@ -1391,10 +1424,14 @@
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason);
+ options.displayId = motionEntry.displayId;
+ options.deviceId = motionEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
reason);
+ options.displayId = motionEntry.displayId;
+ options.deviceId = motionEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
break;
@@ -1467,7 +1504,7 @@
void InputDispatcher::drainInboundQueueLocked() {
while (!mInboundQueue.empty()) {
- std::shared_ptr<EventEntry> entry = mInboundQueue.front();
+ std::shared_ptr<const EventEntry> entry = mInboundQueue.front();
mInboundQueue.pop_front();
releaseInboundEventLocked(entry);
}
@@ -1481,8 +1518,8 @@
}
}
-void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
- InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<const EventEntry> entry) {
+ const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("Injected inbound event was dropped.");
@@ -1502,16 +1539,17 @@
}
std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
- std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
+ std::shared_ptr<const KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
uint32_t policyFlags = entry->policyFlags &
(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;
@@ -1575,24 +1613,23 @@
// This event should go to the front of the queue, but behind all other focus events
// Find the last focus event, and insert right after it
- std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
- std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
- [](const std::shared_ptr<EventEntry>& event) {
- return event->type == EventEntry::Type::FOCUS;
- });
+ auto it = std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
+ [](const std::shared_ptr<const EventEntry>& event) {
+ return event->type == EventEntry::Type::FOCUS;
+ });
// Maintain the order of focus events. Insert the entry after all other focus events.
mInboundQueue.insert(it.base(), std::move(focusEntry));
}
-void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime,
+ std::shared_ptr<const FocusEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
channel->getName();
@@ -1602,7 +1639,7 @@
}
void InputDispatcher::dispatchPointerCaptureChangedLocked(
- nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+ nsecs_t currentTime, const std::shared_ptr<const PointerCaptureChangedEntry>& entry,
DropReason& dropReason) {
dropReason = DropReason::NOT_DROPPED;
@@ -1666,15 +1703,14 @@
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
dispatchEventLocked(currentTime, entry, {target});
dropReason = DropReason::NOT_DROPPED;
}
-void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
- const std::shared_ptr<TouchModeEntry>& entry) {
+void InputDispatcher::dispatchTouchModeChangeLocked(
+ nsecs_t currentTime, const std::shared_ptr<const TouchModeEntry>& entry) {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
getWindowHandlesLocked(entry->displayId);
if (windowHandles.empty()) {
@@ -1703,14 +1739,13 @@
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
inputTargets.push_back(target);
}
return inputTargets;
}
-bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
+bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
+ DropReason* dropReason, nsecs_t& nextWakeupTime) {
// Preprocessing.
if (!entry->dispatchInProgress) {
if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&
@@ -1761,9 +1796,7 @@
// Handle case where the policy asked us to try again later last time.
if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
if (currentTime < entry->interceptKeyWakeupTime) {
- if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
- *nextWakeupTime = entry->interceptKeyWakeupTime;
- }
+ nextWakeupTime = std::min(nextWakeupTime, entry->interceptKeyWakeupTime);
return false; // wait until next wakeup
}
entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
@@ -1819,9 +1852,8 @@
LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
std::vector<InputTarget> inputTargets;
- addWindowTargetLocked(focusedWindow,
- InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS,
- getDownTime(*entry), inputTargets);
+ addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
+ InputTarget::Flags::FOREGROUND, getDownTime(*entry), inputTargets);
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1843,8 +1875,8 @@
}
void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
- const std::shared_ptr<SensorEntry>& entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
+ const std::shared_ptr<const SensorEntry>& entry,
+ DropReason* dropReason, nsecs_t& nextWakeupTime) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
"source=0x%x, sensorType=%s",
@@ -1872,7 +1904,7 @@
std::scoped_lock _l(mLock);
for (auto it = mInboundQueue.begin(); it != mInboundQueue.end(); it++) {
- std::shared_ptr<EventEntry> entry = *it;
+ std::shared_ptr<const EventEntry> entry = *it;
if (entry->type == EventEntry::Type::SENSOR) {
it = mInboundQueue.erase(it);
releaseInboundEventLocked(entry);
@@ -1882,8 +1914,9 @@
return true;
}
-bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) {
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime,
+ std::shared_ptr<const MotionEntry> entry,
+ DropReason* dropReason, nsecs_t& nextWakeupTime) {
ATRACE_CALL();
// Preprocessing.
if (!entry->dispatchInProgress) {
@@ -1925,10 +1958,9 @@
findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime, injectionResult);
if (injectionResult == InputEventInjectionResult::SUCCEEDED) {
LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
- addWindowTargetLocked(focusedWindow,
- InputTarget::Flags::FOREGROUND |
- InputTarget::Flags::DISPATCH_AS_IS,
- getDownTime(*entry), inputTargets);
+ addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
+ InputTarget::Flags::FOREGROUND, getDownTime(*entry),
+ inputTargets);
}
}
if (injectionResult == InputEventInjectionResult::PENDING) {
@@ -1967,14 +1999,14 @@
enqueueInboundEventLocked(std::move(dragEntry));
}
-void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime,
+ std::shared_ptr<const DragEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
dispatchEventLocked(currentTime, entry, {target});
}
@@ -2013,7 +2045,7 @@
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
- std::shared_ptr<EventEntry> eventEntry,
+ std::shared_ptr<const EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
if (DEBUG_DISPATCH_CYCLE) {
@@ -2032,10 +2064,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.";
}
}
}
@@ -2129,9 +2161,8 @@
}
sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
- nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+ nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
InputEventInjectionResult& outInjectionResult) {
- std::string reason;
outInjectionResult = InputEventInjectionResult::FAILED; // Default result
int32_t displayId = getTargetDisplayId(entry);
@@ -2169,7 +2200,7 @@
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
- *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
+ nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime);
outInjectionResult = InputEventInjectionResult::PENDING;
return nullptr;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
@@ -2214,7 +2245,7 @@
// prior input events.
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
- *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
+ nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout);
outInjectionResult = InputEventInjectionResult::PENDING;
return nullptr;
}
@@ -2300,6 +2331,13 @@
}
if (isHoverAction) {
+ if (wasDown) {
+ // Started hovering, but the device is already down: reject the hover event
+ LOG(ERROR) << "Got hover event " << entry.getDescription()
+ << " but the device is already down " << oldState->dump();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
// For hover actions, we will treat 'tempTouchState' as a new state, so let's erase
// all of the existing hovering pointers and recompute.
tempTouchState.clearHoveringPointers(entry.deviceId);
@@ -2309,7 +2347,7 @@
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
- const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
+ const PointerProperties& pointer = entry.pointerProperties[pointerIndex];
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2317,7 +2355,7 @@
findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (isDown) {
- targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId);
+ targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
@@ -2375,11 +2413,11 @@
if (isHoverAction) {
// The "windowHandle" is the target of this hovering pointer.
- tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
+ tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer);
}
// Set target flags.
- ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
+ ftl::Flags<InputTarget::Flags> targetFlags;
if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
// There should only be one touched window that can be "foreground" for the pointer.
@@ -2398,12 +2436,10 @@
// Update the temporary touch state.
if (!isHoverAction) {
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(pointerId);
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
- tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, entry.deviceId,
- pointerIds,
+ tempTouchState.addOrUpdateWindow(windowHandle, InputTarget::DispatchMode::AS_IS,
+ targetFlags, entry.deviceId, {pointer},
isDownOrPointerDown
? std::make_optional(entry.eventTime)
: std::nullopt);
@@ -2420,13 +2456,14 @@
if (wallpaper != nullptr) {
ftl::Flags<InputTarget::Flags> wallpaperFlags =
InputTarget::Flags::WINDOW_IS_OBSCURED |
- InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED |
- InputTarget::Flags::DISPATCH_AS_IS;
+ InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
if (isSplit) {
wallpaperFlags |= InputTarget::Flags::SPLIT;
}
- tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, entry.deviceId,
- pointerIds, entry.eventTime);
+ tempTouchState.addOrUpdateWindow(wallpaper,
+ InputTarget::DispatchMode::AS_IS,
+ wallpaperFlags, entry.deviceId, {pointer},
+ entry.eventTime);
}
}
}
@@ -2436,12 +2473,12 @@
// make it pilfering. This will prevent other non-spy windows from getting this pointer,
// which is a specific behaviour that we want.
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
+ if (touchedWindow.hasTouchingPointer(entry.deviceId, pointer.id) &&
touchedWindow.hasPilferingPointers(entry.deviceId)) {
// This window is already pilfering some pointers, and this new pointer is also
// going to it. Therefore, take over this pointer and don't give it to anyone
// else.
- touchedWindow.addPilferingPointer(entry.deviceId, pointerId);
+ touchedWindow.addPilferingPointer(entry.deviceId, pointer.id);
}
}
@@ -2453,10 +2490,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 {};
}
@@ -2509,14 +2547,14 @@
// Make a slippery exit from the old window.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
- const int32_t pointerId = entry.pointerProperties[0].id;
- pointerIds.set(pointerId);
+ const PointerProperties& pointer = entry.pointerProperties[0];
+ pointerIds.set(pointer.id);
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
addPointerWindowTargetLocked(oldTouchedWindowHandle,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- pointerIds,
+ InputTarget::DispatchMode::SLIPPERY_EXIT,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
touchedWindow.getDownTimeInTarget(entry.deviceId),
targets);
@@ -2525,8 +2563,7 @@
isSplit = !isFromMouse;
}
- ftl::Flags<InputTarget::Flags> targetFlags =
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER;
+ ftl::Flags<InputTarget::Flags> targetFlags;
if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -2539,13 +2576,15 @@
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
- tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags,
- entry.deviceId, pointerIds, entry.eventTime);
+ tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
+ InputTarget::DispatchMode::SLIPPERY_ENTER,
+ targetFlags, entry.deviceId, {pointer},
+ entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry.deviceId, pointerId, targets);
- tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointerId,
+ tempTouchState, entry.deviceId, pointer, targets);
+ tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
}
@@ -2554,14 +2593,12 @@
if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
// If no split, we suppose all touched windows should receive pointer down.
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
- for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ std::vector<PointerProperties> touchingPointers{entry.pointerProperties[pointerIndex]};
+ for (TouchedWindow& touchedWindow : tempTouchState.windows) {
// Ignore drag window for it should just track one pointer.
if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
continue;
}
- std::bitset<MAX_POINTER_ID + 1> touchingPointers;
- touchingPointers.set(entry.pointerProperties[pointerIndex].id);
touchedWindow.addTouchingPointers(entry.deviceId, touchingPointers);
}
}
@@ -2573,7 +2610,8 @@
getHoveringWindowsLocked(oldState, tempTouchState, entry);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
std::optional<InputTarget> target =
- createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags,
touchedWindow.getDownTimeInTarget(entry.deviceId));
if (!target) {
continue;
@@ -2610,7 +2648,7 @@
if (foregroundWindowHandle) {
const auto foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
for (InputTarget& target : targets) {
- if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
+ if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
sp<WindowInfoHandle> targetWindow =
getWindowHandleLocked(target.inputChannel->getConnectionToken());
if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
@@ -2631,13 +2669,13 @@
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- std::bitset<MAX_POINTER_ID + 1> touchingPointers =
+ std::vector<PointerProperties> touchingPointers =
touchedWindow.getTouchingPointers(entry.deviceId);
- if (touchingPointers.none()) {
+ if (touchingPointers.empty()) {
continue;
}
- addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchingPointers,
+ addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, getPointerIds(touchingPointers),
touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
}
@@ -2662,7 +2700,7 @@
// If we only have windows getting ACTION_OUTSIDE, then drop the event, because there is no
// window that is actually receiving the entire gesture.
if (std::all_of(targets.begin(), targets.end(), [](const InputTarget& target) {
- return target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE);
+ return target.dispatchMode == InputTarget::DispatchMode::OUTSIDE;
})) {
LOG(INFO) << "Dropping event because all windows would just receive ACTION_OUTSIDE: "
<< entry.getDescription();
@@ -2672,24 +2710,14 @@
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
+ // Now that we have generated all of the input targets for this event, reset the dispatch
+ // mode for all touched window to AS_IS.
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
- // Targets that we entered in a slippery way will now become AS-IS targets
- if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
- touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER);
- touchedWindow.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS;
- }
+ touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
}
// Update final pieces of touch state if the injector had permission.
- if (isHoverAction) {
- if (oldState && oldState->isDown(entry.deviceId)) {
- // Started hovering, but the device is already down: reject the hover event
- LOG(ERROR) << "Got hover event " << entry.getDescription()
- << " but the device is already down " << oldState->dump();
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
+ if (maskedAction == AMOTION_EVENT_ACTION_UP) {
// Pointer went up.
tempTouchState.removeTouchingPointer(entry.deviceId, entry.pointerProperties[0].id);
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
@@ -2818,7 +2846,7 @@
std::optional<InputTarget> InputDispatcher::createInputTargetLocked(
const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags,
+ InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
std::optional<nsecs_t> firstDownTimeInTarget) const {
std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
if (inputChannel == nullptr) {
@@ -2828,6 +2856,7 @@
InputTarget inputTarget;
inputTarget.inputChannel = inputChannel;
inputTarget.windowHandle = windowHandle;
+ inputTarget.dispatchMode = dispatchMode;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
@@ -2842,6 +2871,7 @@
}
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const {
@@ -2856,7 +2886,8 @@
if (it == inputTargets.end()) {
std::optional<InputTarget> target =
- createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
+ createInputTargetLocked(windowHandle, dispatchMode, targetFlags,
+ firstDownTimeInTarget);
if (!target) {
return;
}
@@ -2864,15 +2895,20 @@
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(
const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags, std::bitset<MAX_POINTER_ID + 1> pointerIds,
- std::optional<nsecs_t> firstDownTimeInTarget, std::vector<InputTarget>& inputTargets) const
- REQUIRES(mLock) {
+ InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget,
+ std::vector<InputTarget>& inputTargets) const REQUIRES(mLock) {
if (pointerIds.none()) {
for (const auto& target : inputTargets) {
LOG(INFO) << "Target: " << target;
@@ -2893,7 +2929,7 @@
// input targets for hovering pointers and for touching pointers.
// If we picked an existing input target above, but it's for HOVER_EXIT - let's use a new
// target instead.
- if (it != inputTargets.end() && it->flags.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
+ if (it != inputTargets.end() && it->dispatchMode == InputTarget::DispatchMode::HOVER_EXIT) {
// Force the code below to create a new input target
it = inputTargets.end();
}
@@ -2902,7 +2938,8 @@
if (it == inputTargets.end()) {
std::optional<InputTarget> target =
- createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
+ createInputTargetLocked(windowHandle, dispatchMode, targetFlags,
+ firstDownTimeInTarget);
if (!target) {
return;
}
@@ -2910,8 +2947,18 @@
it = inputTargets.end() - 1;
}
- LOG_ALWAYS_FATAL_IF(it->flags != targetFlags);
- LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor);
+ if (it->dispatchMode != dispatchMode) {
+ LOG(ERROR) << __func__ << ": DispatchMode doesn't match! ignoring new mode="
+ << ftl::enum_string(dispatchMode) << ", it=" << *it;
+ }
+ if (it->flags != targetFlags) {
+ LOG(ERROR) << __func__ << ": Flags don't match! new targetFlags=" << targetFlags.string()
+ << ", it=" << *it;
+ }
+ if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+ LOG(ERROR) << __func__ << ": Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
+ << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+ }
it->addPointers(pointerIds, windowInfo->transform);
}
@@ -2924,7 +2971,6 @@
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
InputTarget target;
target.inputChannel = monitor.inputChannel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
// target.firstDownTimeInTarget is not set for global monitors. It is only required in split
// touch and global monitoring works as intended even without setting firstDownTimeInTarget
if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
@@ -3196,7 +3242,7 @@
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry> eventEntry,
+ std::shared_ptr<const EventEntry> eventEntry,
const InputTarget& inputTarget) {
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
@@ -3251,41 +3297,29 @@
connection->getInputChannelName().c_str());
logOutboundMotionDetails(" ", *splitMotionEntry);
}
- enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),
- inputTarget);
+ enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection,
+ std::move(splitMotionEntry),
+ inputTarget);
return;
}
}
// Not splitting. Enqueue dispatch entries for the event as is.
- enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
+ enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection, eventEntry,
+ inputTarget);
}
-void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry> eventEntry,
- const InputTarget& inputTarget) {
+void InputDispatcher::enqueueDispatchEntryAndStartDispatchCycleLocked(
+ nsecs_t currentTime, const std::shared_ptr<Connection>& connection,
+ std::shared_ptr<const EventEntry> eventEntry, const InputTarget& inputTarget) {
ATRACE_NAME_IF(ATRACE_ENABLED(),
- StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",
+ StringPrintf("enqueueDispatchEntryAndStartDispatchCycleLocked(inputChannel=%s, "
+ "id=0x%" PRIx32 ")",
connection->getInputChannelName().c_str(), eventEntry->id));
- LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK),
- "No dispatch flags are set for %s", eventEntry->getDescription().c_str());
const bool wasEmpty = connection->outboundQueue.empty();
- // Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::Flags::DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::Flags::DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::Flags::DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::Flags::DISPATCH_AS_IS);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER);
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.empty()) {
@@ -3294,31 +3328,21 @@
}
void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry> eventEntry,
- const InputTarget& inputTarget,
- ftl::Flags<InputTarget::Flags> dispatchMode) {
- ftl::Flags<InputTarget::Flags> inputTargetFlags = inputTarget.flags;
- if (!inputTargetFlags.any(dispatchMode)) {
- return;
- }
-
- inputTargetFlags.clear(InputTarget::DISPATCH_MASK);
- inputTargetFlags |= dispatchMode;
-
+ std::shared_ptr<const EventEntry> eventEntry,
+ const InputTarget& inputTarget) {
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
- createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
+ createDispatchEntry(inputTarget, eventEntry, inputTarget.flags);
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
- EventEntry& newEntry = *(dispatchEntry->eventEntry);
+ eventEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
- switch (newEntry.type) {
+ switch (eventEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
- if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
- dispatchEntry->resolvedFlags)) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
+ if (!connection->inputState.trackKey(keyEntry, keyEntry.flags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
<< "~ dropping inconsistent event: " << *dispatchEntry;
return; // skip the inconsistent event
@@ -3327,94 +3351,128 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);
- // Assign a default value to dispatchEntry that will never be generated by InputReader,
- // and assign a InputDispatcher value if it doesn't change in the if-else chain below.
- constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
- static_cast<int32_t>(IdGenerator::Source::OTHER);
- dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
- if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_ENTER)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
- } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
- } else {
- dispatchEntry->resolvedEventId = motionEntry.id;
- }
- if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
- !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId)) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover "
- "enter event",
- connection->getInputChannelName().c_str());
- }
- // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
- // this is a one-to-one event conversion.
- dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- }
+ std::shared_ptr<const MotionEntry> resolvedMotion =
+ std::static_pointer_cast<const MotionEntry>(eventEntry);
+ {
+ // Determine the resolved motion entry.
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ int32_t resolvedAction = motionEntry.action;
+ int32_t resolvedFlags = motionEntry.flags;
- if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
- }
- if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
- }
- if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) {
- dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ if (inputTarget.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
+ resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
+ } else if (inputTarget.dispatchMode == InputTarget::DispatchMode::HOVER_EXIT) {
+ resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
+ } else if (inputTarget.dispatchMode == InputTarget::DispatchMode::HOVER_ENTER) {
+ resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ } else if (inputTarget.dispatchMode == InputTarget::DispatchMode::SLIPPERY_EXIT) {
+ resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
+ } else if (inputTarget.dispatchMode == InputTarget::DispatchMode::SLIPPERY_ENTER) {
+ resolvedAction = AMOTION_EVENT_ACTION_DOWN;
+ }
+ if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
+ !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId)) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ LOG(DEBUG) << "channel '" << connection->getInputChannelName().c_str()
+ << "' ~ enqueueDispatchEntryLocked: filling in missing hover "
+ "enter event";
+ }
+ resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+ }
+
+ if (resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ resolvedFlags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) {
+ resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ }
+ if (dispatchEntry->targetFlags.test(
+ InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) {
+ resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ }
+
+ dispatchEntry->resolvedFlags = resolvedFlags;
+ if (resolvedAction != motionEntry.action) {
+ std::optional<std::vector<PointerProperties>> usingProperties;
+ std::optional<std::vector<PointerCoords>> usingCoords;
+ if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // This is a HOVER_EXIT or an ACTION_CANCEL event that was synthesized by
+ // the dispatcher, and therefore the coordinates of this event are currently
+ // incorrect. These events should use the coordinates of the last dispatched
+ // ACTION_MOVE or HOVER_MOVE. We need to query InputState to get this data.
+ const bool hovering = resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT;
+ std::optional<std::pair<std::vector<PointerProperties>,
+ std::vector<PointerCoords>>>
+ pointerInfo =
+ connection->inputState.getPointersOfLastEvent(motionEntry,
+ hovering);
+ if (pointerInfo) {
+ usingProperties = pointerInfo->first;
+ usingCoords = pointerInfo->second;
+ }
+ }
+ // Generate a new MotionEntry with a new eventId using the resolved action and
+ // flags.
+ resolvedMotion = std::make_shared<
+ MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, resolvedAction,
+ motionEntry.actionButton, resolvedFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime,
+ usingProperties.value_or(motionEntry.pointerProperties),
+ usingCoords.value_or(motionEntry.pointerCoords));
+ if (ATRACE_ENABLED()) {
+ std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
+ ") to MotionEvent(id=0x%" PRIx32 ").",
+ motionEntry.id, resolvedMotion->id);
+ ATRACE_NAME(message.c_str());
+ }
+
+ // Set the resolved motion entry in the DispatchEntry.
+ dispatchEntry->eventEntry = resolvedMotion;
+ eventEntry = resolvedMotion;
+ }
}
// Check if we need to cancel any of the ongoing gestures. We don't support multiple
// devices being active at the same time in the same window, so if a new device is
// active, cancel the gesture from the old device.
-
std::unique_ptr<EventEntry> cancelEvent =
- connection->inputState
- .cancelConflictingInputStream(motionEntry,
- dispatchEntry->resolvedAction);
+ connection->inputState.cancelConflictingInputStream(*resolvedMotion);
if (cancelEvent != nullptr) {
- LOG(INFO) << "Canceling pointers for device " << motionEntry.deviceId << " in "
+ LOG(INFO) << "Canceling pointers for device " << resolvedMotion->deviceId << " in "
<< connection->getInputChannelName() << " with event "
<< cancelEvent->getDescription();
std::unique_ptr<DispatchEntry> cancelDispatchEntry =
createDispatchEntry(inputTarget, std::move(cancelEvent),
- InputTarget::Flags::DISPATCH_AS_IS);
+ ftl::Flags<InputTarget::Flags>());
// Send these cancel events to the queue before sending the event from the new
// device.
connection->outboundQueue.emplace_back(std::move(cancelDispatchEntry));
}
- if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
+ if (!connection->inputState.trackMotion(*resolvedMotion,
dispatchEntry->resolvedFlags)) {
LOG(WARNING) << "channel " << connection->getInputChannelName()
<< "~ dropping inconsistent event: " << *dispatchEntry;
return; // skip the inconsistent event
}
- dispatchEntry->resolvedEventId =
- dispatchEntry->resolvedEventId == DEFAULT_RESOLVED_EVENT_ID
- ? mIdGenerator.nextId()
- : motionEntry.id;
- if (ATRACE_ENABLED() && dispatchEntry->resolvedEventId != motionEntry.id) {
- std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
- ") to MotionEvent(id=0x%" PRIx32 ").",
- motionEntry.id, dispatchEntry->resolvedEventId);
- ATRACE_NAME(message.c_str());
- }
-
- if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
- (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) {
+ if ((resolvedMotion->flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) &&
+ (resolvedMotion->policyFlags & POLICY_FLAG_TRUSTED)) {
// Skip reporting pointer down outside focus to the policy.
break;
}
- dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
+ dispatchPointerDownOutsideFocus(resolvedMotion->source, resolvedMotion->action,
inputTarget.inputChannel->getConnectionToken());
break;
@@ -3432,14 +3490,14 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- ftl::enum_string(newEntry.type).c_str());
+ ftl::enum_string(eventEntry->type).c_str());
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatches(newEntry);
+ incrementPendingForegroundDispatches(*eventEntry);
}
// Enqueue the dispatch entry.
@@ -3494,7 +3552,7 @@
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
std::vector<std::shared_ptr<Connection>> newConnections;
for (const InputTarget& target : targets) {
- if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
+ if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
continue; // Skip windows that receive ACTION_OUTSIDE
}
@@ -3588,18 +3646,17 @@
// Publish the motion event.
return connection.inputPublisher
- .publishMotionEvent(dispatchEntry.seq, dispatchEntry.resolvedEventId,
- motionEntry.deviceId, motionEntry.source, motionEntry.displayId,
- std::move(hmac), dispatchEntry.resolvedAction,
- motionEntry.actionButton, dispatchEntry.resolvedFlags,
- motionEntry.edgeFlags, motionEntry.metaState,
- motionEntry.buttonState, motionEntry.classification,
- dispatchEntry.transform, motionEntry.xPrecision,
- motionEntry.yPrecision, motionEntry.xCursorPosition,
- motionEntry.yCursorPosition, dispatchEntry.rawTransform,
- motionEntry.downTime, motionEntry.eventTime,
- motionEntry.getPointerCount(), motionEntry.pointerProperties.data(),
- usingCoords);
+ .publishMotionEvent(dispatchEntry.seq, motionEntry.id, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId, std::move(hmac),
+ motionEntry.action, motionEntry.actionButton,
+ dispatchEntry.resolvedFlags, motionEntry.edgeFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, dispatchEntry.transform,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ dispatchEntry.rawTransform, motionEntry.downTime,
+ motionEntry.eventTime, motionEntry.getPointerCount(),
+ motionEntry.pointerProperties.data(), usingCoords);
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3631,14 +3688,13 @@
// Publish the key event.
status = connection->inputPublisher
- .publishKeyEvent(dispatchEntry->seq,
- dispatchEntry->resolvedEventId, keyEntry.deviceId,
- keyEntry.source, keyEntry.displayId,
- std::move(hmac), dispatchEntry->resolvedAction,
- dispatchEntry->resolvedFlags, keyEntry.keyCode,
- keyEntry.scanCode, keyEntry.metaState,
- keyEntry.repeatCount, keyEntry.downTime,
- keyEntry.eventTime);
+ .publishKeyEvent(dispatchEntry->seq, keyEntry.id,
+ keyEntry.deviceId, keyEntry.source,
+ keyEntry.displayId, std::move(hmac),
+ keyEntry.action, dispatchEntry->resolvedFlags,
+ keyEntry.keyCode, keyEntry.scanCode,
+ keyEntry.metaState, keyEntry.repeatCount,
+ keyEntry.downTime, keyEntry.eventTime);
break;
}
@@ -3756,7 +3812,7 @@
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
- const int32_t actionMasked = MotionEvent::getActionMasked(dispatchEntry.resolvedAction);
+ const int32_t actionMasked = MotionEvent::getActionMasked(motionEntry.action);
if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) {
// Only sign events up and down events as the purely move events
// are tied to their up/down counterparts so signing would be redundant.
@@ -3774,7 +3830,6 @@
const KeyEntry& keyEntry, const DispatchEntry& dispatchEntry) const {
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
- verifiedEvent.action = dispatchEntry.resolvedAction;
return sign(verifiedEvent);
}
@@ -3948,16 +4003,6 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
- if ((options.mode == CancelationOptions::Mode::CANCEL_POINTER_EVENTS ||
- options.mode == CancelationOptions::Mode::CANCEL_ALL_EVENTS) &&
- mDragState && mDragState->dragWindow->getToken() == connection->inputChannel->getToken()) {
- LOG(INFO) << __func__
- << ": Canceling drag and drop because the pointers for the drag window are being "
- "canceled.";
- sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
- mDragState.reset();
- }
-
if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3970,6 +4015,7 @@
if (cancelationEvents.empty()) {
return;
}
+
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
"with reality: %s, mode=%s.",
@@ -3983,8 +4029,7 @@
const bool wasEmpty = connection->outboundQueue.empty();
// The target to use if we don't find a window associated with the channel.
- const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
- .flags = InputTarget::Flags::DISPATCH_AS_IS};
+ const InputTarget fallbackTarget{.inputChannel = connection->inputChannel};
const auto& token = connection->inputChannel->getConnectionToken();
for (size_t i = 0; i < cancelationEvents.size(); i++) {
@@ -3998,8 +4043,8 @@
? std::make_optional(keyEntry.displayId)
: std::nullopt;
if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
- addWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
- keyEntry.downTime, targets);
+ addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS,
+ /*targetFlags=*/{}, keyEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
}
@@ -4018,8 +4063,17 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addPointerWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS,
- pointerIds, motionEntry.downTime, targets);
+ if (mDragState && mDragState->dragWindow->getToken() == token &&
+ pointerIds.test(mDragState->pointerId)) {
+ LOG(INFO) << __func__
+ << ": Canceling drag and drop because the pointers for the drag "
+ "window are being canceled.";
+ sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
+ mDragState.reset();
+ }
+ addPointerWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ motionEntry.downTime, targets);
} else {
targets.emplace_back(fallbackTarget);
const auto it = mDisplayInfos.find(motionEntry.displayId);
@@ -4049,8 +4103,7 @@
}
if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
- enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0],
- InputTarget::Flags::DISPATCH_AS_IS);
+ enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0]);
}
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -4093,8 +4146,9 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addPointerWindowTargetLocked(windowHandle, targetFlags, pointerIds,
- motionEntry.downTime, targets);
+ addPointerWindowTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
+ targetFlags, pointerIds, motionEntry.downTime,
+ targets);
} else {
targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel,
.flags = targetFlags});
@@ -4123,8 +4177,7 @@
}
if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
- enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0],
- InputTarget::Flags::DISPATCH_AS_IS);
+ enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0]);
}
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -4226,7 +4279,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 +4295,6 @@
originalMotionEntry.yCursorPosition, splitDownTime,
splitPointerProperties, splitPointerCoords);
- if (originalMotionEntry.injectionState) {
- splitMotionEntry->injectionState = originalMotionEntry.injectionState;
- splitMotionEntry->injectionState->refCount += 1;
- }
-
return splitMotionEntry;
}
@@ -4333,9 +4382,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();
@@ -4404,7 +4454,8 @@
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
- mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(args.displayId, args.source, args.action, args.eventTime,
+ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4419,7 +4470,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 +4504,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 +4657,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 +4692,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));
@@ -4660,7 +4716,9 @@
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
nsecs_t eventTime = motionEvent.getEventTime();
android::base::Timer t;
- mPolicy.interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(displayId, motionEvent.getSource(),
+ motionEvent.getAction(), eventTime,
+ /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4680,9 +4738,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 +4761,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 +4786,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 +4848,6 @@
}
}
}
-
- injectionState->release();
} // release lock
if (DEBUG_INJECTION) {
@@ -4841,39 +4891,42 @@
return result;
}
-void InputDispatcher::setInjectionResult(EventEntry& entry,
+void InputDispatcher::setInjectionResult(const 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(
@@ -4899,19 +4952,17 @@
}
}
-void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches += 1;
+void InputDispatcher::incrementPendingForegroundDispatches(const EventEntry& entry) {
+ if (entry.injectionState) {
+ entry.injectionState->pendingForegroundDispatches += 1;
}
}
-void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
- InjectionState* injectionState = entry.injectionState;
- if (injectionState) {
- injectionState->pendingForegroundDispatches -= 1;
+void InputDispatcher::decrementPendingForegroundDispatches(const EventEntry& entry) {
+ if (entry.injectionState) {
+ entry.injectionState->pendingForegroundDispatches -= 1;
- if (injectionState->pendingForegroundDispatches == 0) {
+ if (entry.injectionState->pendingForegroundDispatches == 0) {
mInjectionSyncFinished.notify_all();
}
}
@@ -5035,6 +5086,13 @@
return false;
}
+ // Ignore touches if stylus is down anywhere on screen
+ if (info.inputConfig.test(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH) &&
+ isStylusActiveInDisplay(info.displayId, mTouchStatesByDisplay)) {
+ LOG(INFO) << "Dropping touch from " << window->getName() << " because stylus is active";
+ return false;
+ }
+
return true;
}
@@ -5113,7 +5171,7 @@
for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
windowList += iwh->getName() + " ";
}
- ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
+ LOG(INFO) << "setInputWindows displayId=" << displayId << " " << windowList;
}
// Check preconditions for new input windows
@@ -5469,29 +5527,29 @@
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = touchedWindow->getTouchingPointers(deviceId);
+ std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId);
sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
// Add new window.
nsecs_t downTimeInTarget = now();
ftl::Flags<InputTarget::Flags> newTargetFlags =
- oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS);
+ oldTargetFlags & (InputTarget::Flags::SPLIT);
if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
newTargetFlags |= InputTarget::Flags::FOREGROUND;
}
- state->addOrUpdateWindow(toWindowHandle, newTargetFlags, deviceId, pointerIds,
- downTimeInTarget);
+ state->addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags,
+ deviceId, pointers, downTimeInTarget);
// Store the dragging window.
if (isDragDrop) {
- if (pointerIds.count() != 1) {
+ if (pointers.size() != 1) {
ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
" pointer on the window.");
return false;
}
// Track the pointer id for drag window and generate the drag state.
- const size_t id = firstMarkedBit(pointerIds);
+ const size_t id = pointers.begin()->id;
mDragState = std::make_unique<DragState>(toWindowHandle, id);
}
@@ -5508,7 +5566,7 @@
// Check if the wallpaper window should deliver the corresponding event.
transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
- *state, deviceId, pointerIds);
+ *state, deviceId, pointers);
}
} // release lock
@@ -5671,33 +5729,8 @@
if (!windowHandles.empty()) {
dump += INDENT2 "Windows:\n";
for (size_t i = 0; i < windowHandles.size(); i++) {
- const sp<WindowInfoHandle>& windowHandle = windowHandles[i];
- const WindowInfo* windowInfo = windowHandle->getInfo();
-
- dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
- "inputConfig=%s, alpha=%.2f, "
- "frame=[%d,%d][%d,%d], globalScale=%f, "
- "applicationInfo.name=%s, "
- "applicationInfo.token=%s, "
- "touchableRegion=",
- i, windowInfo->name.c_str(), windowInfo->id,
- windowInfo->displayId,
- windowInfo->inputConfig.string().c_str(),
- windowInfo->alpha, windowInfo->frame.left,
- windowInfo->frame.top, windowInfo->frame.right,
- windowInfo->frame.bottom, windowInfo->globalScaleFactor,
- windowInfo->applicationInfo.name.c_str(),
- binderToString(windowInfo->applicationInfo.token).c_str());
- dump += dumpRegion(windowInfo->touchableRegion);
- dump += StringPrintf(", ownerPid=%s, ownerUid=%s, dispatchingTimeout=%" PRId64
- "ms, hasToken=%s, "
- "touchOcclusionMode=%s\n",
- windowInfo->ownerPid.toString().c_str(),
- windowInfo->ownerUid.toString().c_str(),
- millis(windowInfo->dispatchingTimeout),
- binderToString(windowInfo->token).c_str(),
- toString(windowInfo->touchOcclusionMode).c_str());
- windowInfo->transform.dump(dump, "transform", INDENT4);
+ dump += StringPrintf(INDENT3 "%zu: %s", i,
+ streamableToString(*windowHandles[i]).c_str());
}
} else {
dump += INDENT2 "Windows: <none>\n";
@@ -5721,7 +5754,7 @@
// Dump recently dispatched or dropped events from oldest to newest.
if (!mRecentQueue.empty()) {
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
- for (const std::shared_ptr<EventEntry>& entry : mRecentQueue) {
+ for (const std::shared_ptr<const EventEntry>& entry : mRecentQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5744,7 +5777,7 @@
// Dump inbound events from oldest to newest.
if (!mInboundQueue.empty()) {
dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
- for (const std::shared_ptr<EventEntry>& entry : mInboundQueue) {
+ for (const std::shared_ptr<const EventEntry>& entry : mInboundQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5786,17 +5819,19 @@
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
}
- std::stringstream inputStateDump;
- inputStateDump << connection->inputState;
- if (!isEmpty(inputStateDump)) {
+ std::string inputStateDump = streamableToString(connection->inputState);
+ if (!inputStateDump.empty()) {
dump += INDENT3 "InputState: ";
- dump += inputStateDump.str() + "\n";
+ dump += inputStateDump + "\n";
}
}
} else {
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()));
@@ -5857,7 +5892,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
- int fd = serverChannel->getFd();
+ auto&& fd = serverChannel->getFd();
std::shared_ptr<Connection> connection =
std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false,
mIdGenerator);
@@ -5870,7 +5905,7 @@
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
+ mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
nullptr);
} // release lock
@@ -5900,7 +5935,7 @@
std::shared_ptr<Connection> connection =
std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator);
const sp<IBinder>& token = serverChannel->getConnectionToken();
- const int fd = serverChannel->getFd();
+ auto&& fd = serverChannel->getFd();
if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
ALOGE("Created a new connection, but the token %p is already known", token.get());
@@ -5911,7 +5946,7 @@
mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
+ mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
nullptr);
}
@@ -5950,7 +5985,7 @@
removeMonitorChannelLocked(connectionToken);
}
- mLooper->removeFd(connection->inputChannel->getFd());
+ mLooper->removeFd(connection->inputChannel->getFd().get());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -6008,8 +6043,10 @@
"input channel stole pointer stream");
options.deviceId = deviceId;
options.displayId = displayId;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
+ std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId);
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = getPointerIds(pointers);
options.pointerIds = pointerIds;
+
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
const std::shared_ptr<InputChannel> channel =
@@ -6128,7 +6165,7 @@
uint32_t seq, bool handled,
nsecs_t consumeTime) {
// Handle post-event policy actions.
- bool restartEvent;
+ std::unique_ptr<const KeyEntry> fallbackKeyEntry;
{ // Start critical section
auto dispatchEntryIt =
@@ -6152,15 +6189,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 +6215,12 @@
}
}
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);
}
+ releaseDispatchEntry(std::move(dispatchEntry));
}
// Start the next dispatch cycle for this connection.
@@ -6289,7 +6320,7 @@
}
void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
- KeyEntry& entry) {
+ const KeyEntry& entry) {
const KeyEvent event = createKeyEvent(entry);
nsecs_t delay = 0;
{ // release lock
@@ -6374,15 +6405,15 @@
sendWindowResponsiveCommandLocked(connectionToken, pid);
}
-bool InputDispatcher::afterKeyEventLockedInterruptable(
+std::unique_ptr<const 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 +6473,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 +6498,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 +6559,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 +6584,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() {
@@ -6820,10 +6842,10 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t deviceId, int32_t pointerId,
+ TouchState& state, int32_t deviceId,
+ const PointerProperties& pointerProperties,
std::vector<InputTarget>& targets) const {
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(pointerId);
+ std::vector<PointerProperties> pointers{pointerProperties};
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) &&
@@ -6839,20 +6861,17 @@
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
- addPointerWindowTargetLocked(oldWallpaper,
- oldTouchedWindow.targetFlags |
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- pointerIds, oldTouchedWindow.getDownTimeInTarget(deviceId),
- targets);
- state.removeTouchingPointerFromWindow(deviceId, pointerId, oldWallpaper);
+ addPointerWindowTargetLocked(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
+ oldTouchedWindow.targetFlags, getPointerIds(pointers),
+ oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+ state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
}
if (newWallpaper != nullptr) {
- state.addOrUpdateWindow(newWallpaper,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER |
- InputTarget::Flags::WINDOW_IS_OBSCURED |
+ state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
+ InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointerIds);
+ deviceId, pointers);
}
}
@@ -6861,7 +6880,7 @@
const sp<WindowInfoHandle> fromWindowHandle,
const sp<WindowInfoHandle> toWindowHandle,
TouchState& state, int32_t deviceId,
- std::bitset<MAX_POINTER_ID + 1> pointerIds) {
+ const std::vector<PointerProperties>& pointers) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -6886,12 +6905,11 @@
if (newWallpaper != nullptr) {
nsecs_t downTimeInTarget = now();
- ftl::Flags<InputTarget::Flags> wallpaperFlags =
- oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS);
+ ftl::Flags<InputTarget::Flags> wallpaperFlags = oldTargetFlags & InputTarget::Flags::SPLIT;
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
- state.addOrUpdateWindow(newWallpaper, wallpaperFlags, deviceId, pointerIds,
- downTimeInTarget);
+ state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
+ deviceId, pointers, downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
getConnectionLocked(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
@@ -6925,11 +6943,29 @@
return nullptr;
}
-void InputDispatcher::setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
+void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
+ std::chrono::nanoseconds delay) {
std::scoped_lock _l(mLock);
- mConfig.keyRepeatTimeout = timeout;
- mConfig.keyRepeatDelay = delay;
+ mConfig.keyRepeatTimeout = timeout.count();
+ mConfig.keyRepeatDelay = delay.count();
+}
+
+bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token, int32_t displayId,
+ DeviceId deviceId, int32_t pointerId) {
+ std::scoped_lock _l(mLock);
+ auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt == mTouchStatesByDisplay.end()) {
+ return false;
+ }
+ for (const TouchedWindow& window : touchStateIt->second.windows) {
+ if (window.windowHandle->getToken() == token &&
+ (window.hasTouchingPointer(deviceId, pointerId) ||
+ window.hasHoveringPointer(deviceId, pointerId))) {
+ return true;
+ }
+ }
+ return false;
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a1127a0..3567288 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;
@@ -147,7 +145,11 @@
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
- void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) override;
+ void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
+ std::chrono::nanoseconds delay) override;
+
+ bool isPointerInWindow(const sp<IBinder>& token, int32_t displayId, DeviceId deviceId,
+ int32_t pointerId) override;
private:
enum class DropReason {
@@ -172,9 +174,9 @@
sp<Looper> mLooper;
- std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
- std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
- std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
+ std::shared_ptr<const EventEntry> mPendingEvent GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<const EventEntry>> mInboundQueue GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<const EventEntry>> mRecentQueue GUARDED_BY(mLock);
// A command entry captures state and behavior for an action to be performed in the
// dispatch loop after the initial processing has taken place. It is essentially
@@ -207,7 +209,7 @@
// This method should only be called on the input dispatcher's own thread.
void dispatchOnce();
- void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) REQUIRES(mLock);
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -224,7 +226,7 @@
REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
+ void addRecentEventLocked(std::shared_ptr<const EventEntry> entry) REQUIRES(mLock);
// App switch latency optimization.
bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
@@ -236,7 +238,7 @@
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
- std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
+ std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
int32_t displayId, float x, float y, bool isStylus = false,
@@ -283,19 +285,19 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
- void setInjectionResult(EventEntry& entry,
+ void setInjectionResult(const EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
void transformMotionEntryForInjectionLocked(MotionEntry&,
const ui::Transform& injectedTransform) const
REQUIRES(mLock);
std::condition_variable mInjectionSyncFinished;
- void incrementPendingForegroundDispatches(EventEntry& entry);
- void decrementPendingForegroundDispatches(EventEntry& entry);
+ void incrementPendingForegroundDispatches(const EventEntry& entry);
+ void decrementPendingForegroundDispatches(const EventEntry& entry);
// Key repeat tracking.
struct KeyRepeatState {
- std::shared_ptr<KeyEntry> lastKeyEntry; // or null if no repeat
+ std::shared_ptr<const KeyEntry> lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
} mKeyRepeatState GUARDED_BY(mLock);
@@ -321,7 +323,7 @@
// Inbound event processing.
void drainInboundQueueLocked() REQUIRES(mLock);
void releasePendingEventLocked() REQUIRES(mLock);
- void releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
+ void releaseInboundEventLocked(std::shared_ptr<const EventEntry> entry) REQUIRES(mLock);
// Dispatch state.
bool mDispatchEnabled GUARDED_BY(mLock);
@@ -432,23 +434,24 @@
const ConfigurationChangedEntry& entry) REQUIRES(mLock);
bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
REQUIRES(mLock);
- bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
+ bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
+ DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock);
+ bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry,
+ DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock);
+ void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry)
REQUIRES(mLock);
void dispatchPointerCaptureChangedLocked(
- nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+ nsecs_t currentTime, const std::shared_ptr<const PointerCaptureChangedEntry>& entry,
DropReason& dropReason) REQUIRES(mLock);
void dispatchTouchModeChangeLocked(nsecs_t currentTime,
- const std::shared_ptr<TouchModeEntry>& entry)
+ const std::shared_ptr<const TouchModeEntry>& entry)
REQUIRES(mLock);
- void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
+ void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<const EventEntry> entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
- DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
+ void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry,
+ DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock);
+ void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry)
+ REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
@@ -461,9 +464,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);
@@ -521,7 +521,7 @@
int32_t getTargetDisplayId(const EventEntry& entry);
sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
- nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+ nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<InputTarget> findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry,
@@ -531,13 +531,15 @@
std::optional<InputTarget> createInputTargetLocked(
const sp<android::gui::WindowInfoHandle>& windowHandle,
- ftl::Flags<InputTarget::Flags> targetFlags,
+ InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addPointerWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
@@ -581,15 +583,14 @@
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
- REQUIRES(mLock);
- void enqueueDispatchEntriesLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
- REQUIRES(mLock);
+ std::shared_ptr<const EventEntry>,
+ const InputTarget& inputTarget) REQUIRES(mLock);
+ void enqueueDispatchEntryAndStartDispatchCycleLocked(
+ nsecs_t currentTime, const std::shared_ptr<Connection>& connection,
+ std::shared_ptr<const EventEntry>, const InputTarget& inputTarget) REQUIRES(mLock);
void enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
- std::shared_ptr<EventEntry>, const InputTarget& inputTarget,
- ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock);
+ std::shared_ptr<const EventEntry>,
+ const InputTarget& inputTarget) REQUIRES(mLock);
status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
void startDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
@@ -650,7 +651,7 @@
const std::shared_ptr<Connection>& connection, uint32_t seq,
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
- KeyEntry& entry) REQUIRES(mLock);
+ const KeyEntry& entry) REQUIRES(mLock);
void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
REQUIRES(mLock);
@@ -664,12 +665,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<const 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*/>
@@ -691,14 +690,15 @@
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t deviceId, int32_t pointerId,
+ TouchState& state, int32_t deviceId,
+ const PointerProperties& pointerProperties,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
const sp<android::gui::WindowInfoHandle> toWindowHandle,
TouchState& state, int32_t deviceId,
- std::bitset<MAX_POINTER_ID + 1> pointerIds) REQUIRES(mLock);
+ const std::vector<PointerProperties>& pointers) REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 09b5186..1fec9b7 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() {}
@@ -51,8 +38,8 @@
return false;
}
-bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) {
- switch (action) {
+bool InputState::trackKey(const KeyEntry& entry, int32_t flags) {
+ switch (entry.action) {
case AKEY_EVENT_ACTION_UP: {
if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
std::erase_if(mFallbackKeys,
@@ -101,7 +88,7 @@
* true if the incoming event was correctly tracked,
* false if the incoming event should be dropped.
*/
-bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
+bool InputState::trackMotion(const MotionEntry& entry, int32_t flags) {
// Don't track non-pointer events
if (!isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
// This is a focus-dispatched event; we don't track its state.
@@ -113,18 +100,11 @@
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;
}
}
- int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ int32_t actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
case AMOTION_EVENT_ACTION_UP:
case AMOTION_EVENT_ACTION_CANCEL: {
@@ -215,6 +195,16 @@
}
}
+std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>>
+InputState::getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const {
+ ssize_t index = findMotionMemento(entry, hovering);
+ if (index == -1) {
+ return std::nullopt;
+ }
+ return std::make_pair(mMotionMementos[index].pointerProperties,
+ mMotionMementos[index].pointerCoords);
+}
+
ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
for (size_t i = 0; i < mKeyMementos.size(); i++) {
const KeyMemento& memento = mKeyMementos[i];
@@ -301,8 +291,7 @@
return pointerProperties.size();
}
-bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry,
- int32_t resolvedAction) const {
+bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) const {
if (!isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER)) {
// This is a focus-dispatched event that should not affect the previous stream.
return false;
@@ -320,7 +309,7 @@
}
const MotionMemento& lastMemento = mMotionMementos.back();
- const int32_t actionMasked = MotionEvent::getActionMasked(resolvedAction);
+ const int32_t actionMasked = MotionEvent::getActionMasked(motionEntry.action);
// For compatibility, only one input device can be active at a time in the same window.
if (lastMemento.deviceId == motionEntry.deviceId) {
@@ -332,16 +321,6 @@
return true;
}
- // Use the previous stream cancellation logic to generate all HOVER_EXIT events.
- // If this hover event was generated as a result of the pointer leaving the window,
- // the HOVER_EXIT event should have the same coordinates as the previous
- // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same
- // coordinates as the previous event by cancelling the stream here. With this approach, the
- // HOVER_EXIT event is generated from the previous event.
- if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) {
- return true;
- }
-
// If the stream changes its source, just cancel the current gesture to be safe. It's
// possible that the app isn't handling source changes properly
if (motionEntry.source != lastMemento.source) {
@@ -366,19 +345,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;
}
@@ -395,9 +368,9 @@
return false;
}
-std::unique_ptr<EventEntry> InputState::cancelConflictingInputStream(const MotionEntry& motionEntry,
- int32_t resolvedAction) {
- if (!shouldCancelPreviousStream(motionEntry, resolvedAction)) {
+std::unique_ptr<EventEntry> InputState::cancelConflictingInputStream(
+ const MotionEntry& motionEntry) {
+ if (!shouldCancelPreviousStream(motionEntry)) {
return {};
}
@@ -407,7 +380,7 @@
std::unique_ptr<MotionEntry> cancelEntry =
createCancelEntryForMemento(memento, motionEntry.eventTime);
- if (!trackMotion(*cancelEntry, cancelEntry->action, cancelEntry->flags)) {
+ if (!trackMotion(*cancelEntry, cancelEntry->flags)) {
LOG(FATAL) << "Generated inconsistent cancel event!";
}
return cancelEntry;
@@ -421,9 +394,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 +411,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 +473,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 +514,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 +539,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/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 686c432..d49469d 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -41,16 +41,24 @@
// Records tracking information for a key event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
// and should be skipped.
- bool trackKey(const KeyEntry& entry, int32_t action, int32_t flags);
+ bool trackKey(const KeyEntry& entry, int32_t flags);
// Records tracking information for a motion event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
// and should be skipped.
- bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
+ bool trackMotion(const MotionEntry& entry, int32_t flags);
+
+ /**
+ * Return the PointerProperties and the PointerCoords for the last event, if found. Return
+ * std::nullopt if not found. We should not return std::vector<PointerCoords> in isolation,
+ * because the pointers can technically be stored in the vector in any order, so the
+ * PointerProperties are needed to specify the order in which the pointer coords are stored.
+ */
+ std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>>
+ getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const;
// Create cancel events for the previous stream if the current motionEntry requires it.
- std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry,
- int32_t resolvedAction);
+ std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry);
// Synthesizes cancelation events for the current state and resets the tracked state.
std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
@@ -127,7 +135,7 @@
static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
- bool shouldCancelPreviousStream(const MotionEntry& motionEntry, int32_t resolvedAction) const;
+ bool shouldCancelPreviousStream(const MotionEntry& motionEntry) const;
std::unique_ptr<MotionEntry> createCancelEntryForMemento(const MotionMemento& memento,
nsecs_t eventTime) const;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 343630c..c02c5d6 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -95,6 +95,7 @@
} else {
out << "<null>";
}
+ out << ", dispatchMode=" << ftl::enum_string(target.dispatchMode).c_str();
out << ", targetFlags=" << target.flags.string();
out << ", pointers=" << target.getPointerInfoString();
out << "}";
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 8b8a35a..aef866b 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -51,46 +51,39 @@
* the same UID from watching all touches. */
ZERO_COORDS = 1 << 3,
- /* This flag indicates that the event should be sent as is.
- * Should always be set unless the event is to be transmuted. */
- DISPATCH_AS_IS = 1 << 8,
-
- /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
- * of the area of this target and so should instead be delivered as an
- * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
- DISPATCH_AS_OUTSIDE = 1 << 9,
-
- /* This flag indicates that a hover sequence is starting in the given window.
- * The event is transmuted into ACTION_HOVER_ENTER. */
- DISPATCH_AS_HOVER_ENTER = 1 << 10,
-
- /* This flag indicates that a hover event happened outside of a window which handled
- * previous hover events, signifying the end of the current hover sequence for that
- * window.
- * The event is transmuted into ACTION_HOVER_ENTER. */
- DISPATCH_AS_HOVER_EXIT = 1 << 11,
-
- /* This flag indicates that the event should be canceled.
- * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
- * outside of a window. */
- DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
-
- /* This flag indicates that the event should be dispatched as an initial down.
- * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
- * into a new window. */
- DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
-
/* This flag indicates that the target of a MotionEvent is partly or wholly
* obscured by another visible window above it. The motion event should be
* delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */
WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
};
- /* Mask for all dispatch modes. */
- static constexpr const ftl::Flags<InputTarget::Flags> DISPATCH_MASK =
- ftl::Flags<InputTarget::Flags>() | Flags::DISPATCH_AS_IS | Flags::DISPATCH_AS_OUTSIDE |
- Flags::DISPATCH_AS_HOVER_ENTER | Flags::DISPATCH_AS_HOVER_EXIT |
- Flags::DISPATCH_AS_SLIPPERY_EXIT | Flags::DISPATCH_AS_SLIPPERY_ENTER;
+ enum class DispatchMode {
+ /* This flag indicates that the event should be sent as is.
+ * Should always be set unless the event is to be transmuted. */
+ AS_IS,
+ /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside
+ * of the area of this target and so should instead be delivered as an
+ * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
+ OUTSIDE,
+ /* This flag indicates that a hover sequence is starting in the given window.
+ * The event is transmuted into ACTION_HOVER_ENTER. */
+ HOVER_ENTER,
+ /* This flag indicates that a hover event happened outside of a window which handled
+ * previous hover events, signifying the end of the current hover sequence for that
+ * window.
+ * The event is transmuted into ACTION_HOVER_ENTER. */
+ HOVER_EXIT,
+ /* This flag indicates that the event should be canceled.
+ * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips
+ * outside of a window. */
+ SLIPPERY_EXIT,
+ /* This flag indicates that the event should be dispatched as an initial down.
+ * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips
+ * into a new window. */
+ SLIPPERY_ENTER,
+
+ ftl_last = SLIPPERY_ENTER,
+ };
// The input channel to be targeted.
std::shared_ptr<InputChannel> inputChannel;
@@ -98,6 +91,9 @@
// Flags for the input target.
ftl::Flags<Flags> flags;
+ // The dispatch mode that should be used for this target.
+ DispatchMode dispatchMode = DispatchMode::AS_IS;
+
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2ead171..f8aa625 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -71,10 +71,11 @@
}
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
- std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
+ const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget) {
- if (touchingPointerIds.none()) {
+ if (touchingPointers.empty()) {
LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
return;
}
@@ -85,14 +86,12 @@
// An alternative design choice here would have been to compare here by token, but to
// store per-pointer transform.
if (touchedWindow.windowHandle == windowHandle) {
+ touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags |= targetFlags;
- if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
- touchedWindow.targetFlags.clear(InputTarget::Flags::DISPATCH_AS_IS);
- }
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
- touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
+ touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
@@ -101,8 +100,9 @@
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
+ touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags = targetFlags;
- touchedWindow.addTouchingPointers(deviceId, touchingPointerIds);
+ touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
@@ -110,17 +110,17 @@
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
- DeviceId deviceId, int32_t hoveringPointerId) {
+ DeviceId deviceId, const PointerProperties& pointer) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, pointer);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
- touchedWindow.addHoveringPointer(deviceId, hoveringPointerId);
+ touchedWindow.addHoveringPointer(deviceId, pointer);
windows.push_back(touchedWindow);
}
@@ -133,20 +133,6 @@
}
}
-void TouchState::filterNonAsIsTouchWindows() {
- for (size_t i = 0; i < windows.size();) {
- TouchedWindow& window = windows[i];
- if (window.targetFlags.any(InputTarget::Flags::DISPATCH_AS_IS |
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
- window.targetFlags.clear(InputTarget::DISPATCH_MASK);
- window.targetFlags |= InputTarget::Flags::DISPATCH_AS_IS;
- i += 1;
- } else {
- windows.erase(windows.begin() + i);
- }
- }
-}
-
void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
const sp<IBinder>& token) {
@@ -248,6 +234,11 @@
});
}
+bool TouchState::hasActiveStylus() const {
+ return std::any_of(windows.begin(), windows.end(),
+ [](const TouchedWindow& window) { return window.hasActiveStylus(); });
+}
+
std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
int32_t pointerId) const {
std::set<sp<WindowInfoHandle>> out;
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index e79c73b..3d534bc 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -44,17 +44,17 @@
void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
const sp<android::gui::WindowInfoHandle>& windowHandle);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
- std::bitset<MAX_POINTER_ID + 1> touchingPointerIds,
+ const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- DeviceId deviceId, int32_t hoveringPointerId);
- void removeHoveringPointer(DeviceId deviceId, int32_t hoveringPointerId);
+ DeviceId deviceId, const PointerProperties& pointer);
+ void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
void clearHoveringPointers(DeviceId deviceId);
void removeAllPointersForDevice(DeviceId deviceId);
void removeWindowByToken(const sp<IBinder>& token);
- void filterNonAsIsTouchWindows();
// Cancel pointers for current set of windows except the window with particular binder token.
void cancelPointersForWindowsExcept(DeviceId deviceId,
@@ -73,6 +73,8 @@
bool isDown(DeviceId deviceId) const;
bool hasHoveringPointers(DeviceId deviceId) const;
+ bool hasActiveStylus() const;
+
std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
DeviceId deviceId, int32_t pointerId) const;
std::string dump() const;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 5367751..037d7c8 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -26,9 +26,20 @@
namespace inputdispatcher {
+namespace {
+
+bool hasPointerId(const std::vector<PointerProperties>& pointers, int32_t pointerId) {
+ return std::find_if(pointers.begin(), pointers.end(),
+ [&pointerId](const PointerProperties& properties) {
+ return properties.id == pointerId;
+ }) != pointers.end();
+}
+
+} // namespace
+
bool TouchedWindow::hasHoveringPointers() const {
for (const auto& [_, state] : mDeviceStates) {
- if (state.hoveringPointerIds.any()) {
+ if (!state.hoveringPointers.empty()) {
return true;
}
}
@@ -42,7 +53,7 @@
}
const DeviceState& state = stateIt->second;
- return state.hoveringPointerIds.any();
+ return !state.hoveringPointers.empty();
}
void TouchedWindow::clearHoveringPointers(DeviceId deviceId) {
@@ -51,7 +62,7 @@
return;
}
DeviceState& state = stateIt->second;
- state.hoveringPointerIds.reset();
+ state.hoveringPointers.clear();
if (!state.hasPointers()) {
mDeviceStates.erase(stateIt);
}
@@ -63,22 +74,40 @@
return false;
}
const DeviceState& state = stateIt->second;
-
- return state.hoveringPointerIds.test(pointerId);
+ return hasPointerId(state.hoveringPointers, pointerId);
}
-void TouchedWindow::addHoveringPointer(DeviceId deviceId, int32_t pointerId) {
- mDeviceStates[deviceId].hoveringPointerIds.set(pointerId);
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) {
+ std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
+ const size_t initialSize = hoveringPointers.size();
+ std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) {
+ return properties.id == pointer.id;
+ });
+ if (hoveringPointers.size() != initialSize) {
+ LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this;
+ }
+ hoveringPointers.push_back(pointer);
}
void TouchedWindow::addTouchingPointers(DeviceId deviceId,
- std::bitset<MAX_POINTER_ID + 1> pointers) {
- mDeviceStates[deviceId].touchingPointerIds |= pointers;
+ const std::vector<PointerProperties>& pointers) {
+ std::vector<PointerProperties>& touchingPointers = mDeviceStates[deviceId].touchingPointers;
+ const size_t initialSize = touchingPointers.size();
+ for (const PointerProperties& pointer : pointers) {
+ std::erase_if(touchingPointers, [&pointer](const PointerProperties& properties) {
+ return properties.id == pointer.id;
+ });
+ }
+ if (touchingPointers.size() != initialSize) {
+ LOG(ERROR) << __func__ << ": " << dumpVector(pointers, streamableToString) << ", device "
+ << deviceId << " already in " << *this;
+ }
+ touchingPointers.insert(touchingPointers.end(), pointers.begin(), pointers.end());
}
bool TouchedWindow::hasTouchingPointers() const {
for (const auto& [_, state] : mDeviceStates) {
- if (state.touchingPointerIds.any()) {
+ if (!state.touchingPointers.empty()) {
return true;
}
}
@@ -86,21 +115,25 @@
}
bool TouchedWindow::hasTouchingPointers(DeviceId deviceId) const {
- return getTouchingPointers(deviceId).any();
+ return !getTouchingPointers(deviceId).empty();
}
bool TouchedWindow::hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const {
- return getTouchingPointers(deviceId).test(pointerId);
+ const auto stateIt = mDeviceStates.find(deviceId);
+ if (stateIt == mDeviceStates.end()) {
+ return false;
+ }
+ const DeviceState& state = stateIt->second;
+ return hasPointerId(state.touchingPointers, pointerId);
}
-std::bitset<MAX_POINTER_ID + 1> TouchedWindow::getTouchingPointers(DeviceId deviceId) const {
+std::vector<PointerProperties> TouchedWindow::getTouchingPointers(DeviceId deviceId) const {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
return {};
}
const DeviceState& state = stateIt->second;
-
- return state.touchingPointerIds;
+ return state.touchingPointers;
}
void TouchedWindow::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
@@ -118,7 +151,10 @@
}
DeviceState& state = stateIt->second;
- state.touchingPointerIds &= ~pointers;
+ std::erase_if(state.touchingPointers, [&pointers](const PointerProperties& properties) {
+ return pointers.test(properties.id);
+ });
+
state.pilferingPointerIds &= ~pointers;
if (!state.hasPointers()) {
@@ -126,20 +162,30 @@
}
}
-std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const {
- std::set<DeviceId> deviceIds;
- for (const auto& [deviceId, _] : mDeviceStates) {
- deviceIds.insert(deviceId);
+bool TouchedWindow::hasActiveStylus() const {
+ for (const auto& [_, state] : mDeviceStates) {
+ for (const PointerProperties& properties : state.touchingPointers) {
+ if (properties.toolType == ToolType::STYLUS) {
+ return true;
+ }
+ }
+ for (const PointerProperties& properties : state.hoveringPointers) {
+ if (properties.toolType == ToolType::STYLUS) {
+ return true;
+ }
+ }
}
- return deviceIds;
+ return false;
}
-std::set<DeviceId> TouchedWindow::getActiveDeviceIds() const {
- std::set<DeviceId> out;
- for (const auto& [deviceId, _] : mDeviceStates) {
- out.emplace(deviceId);
+std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const {
+ std::set<DeviceId> deviceIds;
+ for (const auto& [deviceId, deviceState] : mDeviceStates) {
+ if (!deviceState.touchingPointers.empty()) {
+ deviceIds.insert(deviceId);
+ }
}
- return out;
+ return deviceIds;
}
bool TouchedWindow::hasPilferingPointers(DeviceId deviceId) const {
@@ -204,7 +250,7 @@
}
DeviceState& state = stateIt->second;
- state.touchingPointerIds.reset();
+ state.touchingPointers.clear();
state.pilferingPointerIds.reset();
state.downTimeInTarget.reset();
@@ -220,7 +266,9 @@
}
DeviceState& state = stateIt->second;
- state.hoveringPointerIds.set(pointerId, false);
+ std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) {
+ return properties.id == pointerId;
+ });
if (!state.hasPointers()) {
mDeviceStates.erase(stateIt);
@@ -234,7 +282,7 @@
}
DeviceState& state = stateIt->second;
- state.hoveringPointerIds.reset();
+ state.hoveringPointers.clear();
if (!state.hasPointers()) {
mDeviceStates.erase(stateIt);
@@ -242,11 +290,11 @@
}
std::string TouchedWindow::deviceStateToString(const TouchedWindow::DeviceState& state) {
- return StringPrintf("[touchingPointerIds=%s, "
- "downTimeInTarget=%s, hoveringPointerIds=%s, pilferingPointerIds=%s]",
- bitsetToString(state.touchingPointerIds).c_str(),
+ return StringPrintf("[touchingPointers=%s, "
+ "downTimeInTarget=%s, hoveringPointers=%s, pilferingPointerIds=%s]",
+ dumpVector(state.touchingPointers, streamableToString).c_str(),
toString(state.downTimeInTarget).c_str(),
- bitsetToString(state.hoveringPointerIds).c_str(),
+ dumpVector(state.hoveringPointers, streamableToString).c_str(),
bitsetToString(state.pilferingPointerIds).c_str());
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 6d2283e..0d1531f 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -31,32 +31,26 @@
// Focus tracking for touch.
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
+ InputTarget::DispatchMode dispatchMode = InputTarget::DispatchMode::AS_IS;
ftl::Flags<InputTarget::Flags> targetFlags;
// Hovering
bool hasHoveringPointers() const;
bool hasHoveringPointers(DeviceId deviceId) const;
bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
- void addHoveringPointer(DeviceId deviceId, int32_t pointerId);
+ void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
// Touching
bool hasTouchingPointer(DeviceId deviceId, int32_t pointerId) const;
bool hasTouchingPointers() const;
bool hasTouchingPointers(DeviceId deviceId) const;
- std::bitset<MAX_POINTER_ID + 1> getTouchingPointers(DeviceId deviceId) const;
- void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
+ std::vector<PointerProperties> getTouchingPointers(DeviceId deviceId) const;
+ void addTouchingPointers(DeviceId deviceId, const std::vector<PointerProperties>& pointers);
void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
- /**
- * Get the currently active touching device id. If there isn't exactly 1 touching device, return
- * nullopt.
- */
+ bool hasActiveStylus() const;
std::set<DeviceId> getTouchingDeviceIds() const;
- /**
- * The ids of devices that are currently touching or hovering.
- */
- std::set<DeviceId> getActiveDeviceIds() const;
// Pilfering pointers
bool hasPilferingPointers(DeviceId deviceId) const;
@@ -76,16 +70,16 @@
private:
struct DeviceState {
- std::bitset<MAX_POINTER_ID + 1> touchingPointerIds;
+ std::vector<PointerProperties> touchingPointers;
// The pointer ids of the pointers that this window is currently pilfering, by device
std::bitset<MAX_POINTER_ID + 1> pilferingPointerIds;
// Time at which the first action down occurred on this window, for each device
// NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
// scenario.
std::optional<nsecs_t> downTimeInTarget;
- std::bitset<MAX_POINTER_ID + 1> hoveringPointerIds;
+ std::vector<PointerProperties> hoveringPointers;
- bool hasPointers() const { return touchingPointerIds.any() || hoveringPointerIds.any(); };
+ bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); };
};
std::map<DeviceId, DeviceState> mDeviceStates;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index d099b44..001dc6c 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -221,7 +221,14 @@
/*
* Updates key repeat configuration timeout and delay.
*/
- virtual void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) = 0;
+ virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
+ std::chrono::nanoseconds delay) = 0;
+
+ /*
+ * Determine if a pointer from a device is being dispatched to the given window.
+ */
+ virtual bool isPointerInWindow(const sp<IBinder>& token, int32_t displayId, DeviceId deviceId,
+ int32_t pointerId) = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index af28e48..9e6209b 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>
@@ -96,8 +99,8 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) = 0;
+ virtual void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action,
+ nsecs_t when, uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
@@ -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/InputFilterPolicyInterface.h b/services/inputflinger/include/InputFilterPolicyInterface.h
new file mode 100644
index 0000000..4d39b97
--- /dev/null
+++ b/services/inputflinger/include/InputFilterPolicyInterface.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 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
+
+namespace android {
+
+/**
+ * The InputFilter policy interface.
+ *
+ * This is the interface that InputFilter uses to talk to Input Manager and other system components.
+ */
+class InputFilterPolicyInterface {
+public:
+ virtual ~InputFilterPolicyInterface() = default;
+
+ /**
+ * A callback to notify about sticky modifier state changes when Sticky keys feature is enabled.
+ *
+ * modifierState: Current sticky modifier state which will be sent with all subsequent
+ * KeyEvents. This only includes modifiers that can be 'Sticky' which includes: Meta, Ctrl,
+ * Shift, Alt and AltGr.
+ *
+ * lockedModifierState: Current locked modifier state representing modifiers that don't get
+ * cleared after non-modifier key press. This only includes modifiers that can be 'Sticky' which
+ * includes: Meta, Ctrl, Shift, Alt and AltGr.
+ *
+ * For more information {@see sticky_keys_filter.rs}
+ */
+ virtual void notifyStickyModifierStateChanged(uint32_t modifierState,
+ uint32_t lockedModifierState) = 0;
+};
+
+} // namespace android
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..c44486f 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -22,6 +22,8 @@
namespace android {
+struct SpriteIcon;
+
struct FloatPoint {
float x;
float y;
@@ -48,10 +50,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 +127,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;
@@ -115,6 +137,12 @@
/* Sets the associated display of this pointer. Pointer should show on that display. */
virtual void setDisplayViewport(const DisplayViewport& displayViewport) = 0;
+
+ /* Sets the pointer icon type for mice or styluses. */
+ virtual void updatePointerIcon(PointerIconStyle iconId) = 0;
+
+ /* Sets the custom pointer icon for mice or styluses. */
+ virtual void setCustomPointerIcon(const SpriteIcon& icon) = 0;
};
} // namespace android
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..58e35a6 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,53 @@
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.
+ resolvedViewport = getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
+ } 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/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 9c87c62..2dd05f5 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -131,7 +131,7 @@
bumpGeneration();
}
}
- if (shouldSimulateStylusWithTouch() && outPointer.toolType == ToolType::FINGER) {
+ if (mShouldSimulateStylusWithTouch && outPointer.toolType == ToolType::FINGER) {
outPointer.toolType = ToolType::STYLUS;
}
@@ -177,6 +177,18 @@
mMultiTouchMotionAccumulator.finishSync();
}
+std::list<NotifyArgs> MultiTouchInputMapper::reconfigure(nsecs_t when,
+ const InputReaderConfiguration& config,
+ ConfigurationChanges changes) {
+ const bool simulateStylusWithTouch =
+ sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
+ if (simulateStylusWithTouch != mShouldSimulateStylusWithTouch) {
+ mShouldSimulateStylusWithTouch = simulateStylusWithTouch;
+ bumpGeneration();
+ }
+ return TouchInputMapper::reconfigure(when, config, changes);
+}
+
void MultiTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
@@ -211,14 +223,7 @@
bool MultiTouchInputMapper::hasStylus() const {
return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
- shouldSimulateStylusWithTouch();
-}
-
-bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
- static const bool SIMULATE_STYLUS_WITH_TOUCH =
- sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
- return SIMULATE_STYLUS_WITH_TOUCH &&
- mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
+ mShouldSimulateStylusWithTouch;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 1d788df..5c173f3 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -32,6 +32,9 @@
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
+ const InputReaderConfiguration& config,
+ ConfigurationChanges changes) override;
protected:
void syncTouch(nsecs_t when, RawState* outState) override;
@@ -41,13 +44,6 @@
private:
explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
- // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
- // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
- // It is used to simulate stylus events for debugging and testing on a device that does not
- // support styluses. It can be enabled using
- // "adb shell setprop persist.debug.input.simulate_stylus_with_touch true",
- // and requires a reboot to take effect.
- inline bool shouldSimulateStylusWithTouch() const;
// If the slot is in use, return the bit id. Return std::nullopt otherwise.
std::optional<int32_t> getActiveBitId(const MultiTouchMotionAccumulator::Slot& inSlot);
@@ -58,6 +54,15 @@
int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
bool mStylusMtToolSeen{false};
+
+ // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
+ // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
+ // It is used to simulate stylus events for debugging and testing on a device that does not
+ // support styluses. It can be enabled using
+ // "adb shell setprop debug.input.simulate_stylus_with_touch true".
+ // After enabling, the touchscreen will need to be reconfigured. A reconfiguration usually
+ // happens when turning the screen on/off or by rotating the device orientation.
+ bool mShouldSimulateStylusWithTouch{false};
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index c76fec7..255f02d 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -21,9 +21,11 @@
#include <iterator>
#include <limits>
#include <map>
+#include <mutex>
#include <optional>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <android/input.h>
#include <com_android_input_flags.h>
#include <ftl/enum.h>
@@ -156,13 +158,20 @@
return sAccumulator;
}
- void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].fingers++; }
+ void recordFinger(const TouchpadInputMapper::MetricsIdentifier& id) {
+ std::scoped_lock lock(mLock);
+ mCounters[id].fingers++;
+ }
- void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) { mCounters[id].palms++; }
+ void recordPalm(const TouchpadInputMapper::MetricsIdentifier& id) {
+ std::scoped_lock lock(mLock);
+ mCounters[id].palms++;
+ }
// Checks whether a Gesture struct is for the end of a gesture that we log metrics for, and
// records it if so.
void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) {
+ std::scoped_lock lock(mLock);
switch (gesture.type) {
case kGestureTypeFling:
if (gesture.details.fling.fling_state == GESTURES_FLING_START) {
@@ -200,15 +209,20 @@
void* cookie) {
LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE);
MetricsAccumulator& accumulator = MetricsAccumulator::getInstance();
- accumulator.produceAtoms(outEventList);
- accumulator.resetCounters();
+ accumulator.produceAtomsAndReset(*outEventList);
return AStatsManager_PULL_SUCCESS;
}
- void produceAtoms(AStatsEventList* outEventList) const {
+ void produceAtomsAndReset(AStatsEventList& outEventList) {
+ std::scoped_lock lock(mLock);
+ produceAtomsLocked(outEventList);
+ resetCountersLocked();
+ }
+
+ void produceAtomsLocked(AStatsEventList& outEventList) const REQUIRES(mLock) {
for (auto& [id, counters] : mCounters) {
auto [busId, vendorId, productId, versionId] = id;
- addAStatsEvent(outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
+ addAStatsEvent(&outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
versionId, linuxBusToInputDeviceBusEnum(busId, /*isUsi=*/false),
counters.fingers, counters.palms, counters.twoFingerSwipeGestures,
counters.threeFingerSwipeGestures, counters.fourFingerSwipeGestures,
@@ -216,7 +230,7 @@
}
}
- void resetCounters() { mCounters.clear(); }
+ void resetCountersLocked() REQUIRES(mLock) { mCounters.clear(); }
// Stores the counters for a specific touchpad model. Fields have the same meanings as those of
// the TouchpadUsage atom; see that definition for detailed documentation.
@@ -232,7 +246,10 @@
// Metrics are aggregated by device model and version, so if two devices of the same model and
// version are connected at once, they will have the same counters.
- std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters;
+ std::map<TouchpadInputMapper::MetricsIdentifier, Counters> mCounters GUARDED_BY(mLock);
+
+ // Metrics are pulled by a binder thread, so we need to guard them with a mutex.
+ mutable std::mutex mLock;
};
} // namespace
@@ -246,7 +263,8 @@
mStateConverter(deviceContext, mMotionAccumulator),
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
- mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
+ mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())),
+ mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
RawAbsoluteAxisInfo slotAxisInfo;
deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
@@ -331,31 +349,56 @@
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
mDisplayId = ADISPLAY_ID_NONE;
- if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
+ std::optional<DisplayViewport> resolvedViewport;
+ std::optional<FloatRect> boundsInLogicalDisplay;
+ if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
// This InputDevice is associated with a viewport.
// Only generate events for the associated display.
- const bool mismatchedPointerDisplay =
- (viewport->displayId != mPointerController->getDisplayId());
- if (mismatchedPointerDisplay) {
- ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
- "controller",
- mDeviceContext.getName().c_str());
+ mDisplayId = assocViewport->displayId;
+ resolvedViewport = *assocViewport;
+ if (!mEnablePointerChoreographer) {
+ const bool mismatchedPointerDisplay =
+ (assocViewport->displayId != mPointerController->getDisplayId());
+ if (mismatchedPointerDisplay) {
+ ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
+ "controller",
+ mDeviceContext.getName().c_str());
+ mDisplayId.reset();
+ }
}
- mDisplayId = mismatchedPointerDisplay ? std::nullopt
- : std::make_optional(viewport->displayId);
} else {
// The InputDevice is not associated with a viewport, but it controls the mouse pointer.
- mDisplayId = mPointerController->getDisplayId();
- }
-
- ui::Rotation orientation = ui::ROTATION_0;
- if (mDisplayId.has_value()) {
- if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) {
- orientation = getInverseRotation(viewport->orientation);
+ if (mEnablePointerChoreographer) {
+ // Always use DISPLAY_ID_NONE for touchpad events.
+ // PointerChoreographer will make it target the correct the displayId later.
+ resolvedViewport =
+ getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
+ } else {
+ mDisplayId = mPointerController->getDisplayId();
+ if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
+ resolvedViewport = *v;
+ }
+ if (auto bounds = mPointerController->getBounds(); bounds) {
+ boundsInLogicalDisplay = *bounds;
+ }
}
}
+
mGestureConverter.setDisplayId(mDisplayId);
- mGestureConverter.setOrientation(orientation);
+ mGestureConverter.setOrientation(resolvedViewport
+ ? getInverseRotation(resolvedViewport->orientation)
+ : ui::ROTATION_0);
+
+ if (!boundsInLogicalDisplay) {
+ boundsInLogicalDisplay = 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};
+ }
+ mGestureConverter.setBoundsInLogicalDisplay(*boundsInLogicalDisplay);
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
@@ -423,6 +466,9 @@
if (mPointerCaptured) {
return mCapturedEventConverter.process(*rawEvent);
}
+ if (mMotionAccumulator.getActiveSlotsCount() == 0) {
+ mGestureStartTime = rawEvent->when;
+ }
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
updatePalmDetectionMetrics();
@@ -488,7 +534,7 @@
if (mDisplayId) {
MetricsAccumulator& metricsAccumulator = MetricsAccumulator::getInstance();
for (Gesture& gesture : mGesturesToProcess) {
- out += mGestureConverter.handleGesture(when, readTime, gesture);
+ out += mGestureConverter.handleGesture(when, readTime, mGestureStartTime, gesture);
metricsAccumulator.processGesture(mMetricsId, gesture);
}
}
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index a68ae43..897edca 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -107,10 +107,14 @@
// Tracking IDs for touches that have at some point been reported as palms by the touchpad.
std::set<int32_t> mPalmTrackingIds;
+ const bool mEnablePointerChoreographer;
+
// The display that events generated by this mapper should target. This can be set to
// 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;
+
+ nsecs_t mGestureStartTime{0};
};
} // 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..19788ce 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,20 @@
#include "TouchCursorInputMapperCommon.h"
#include "input/Input.h"
+namespace input_flags = com::android::input::flags;
+
namespace android {
namespace {
+// This will disable the tap to click while the user is typing on a physical keyboard
+const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
+
+// In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in
+// re-enabling the tap to click.
+const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 =
+ input_flags::enable_v2_touchpad_typing_palm_rejection();
+
uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
switch (gesturesButton) {
case GESTURES_BUTTON_LEFT:
@@ -55,7 +66,8 @@
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mPointerController(readerContext.getPointerController(deviceId)) {
+ mPointerController(readerContext.getPointerController(deviceId)),
+ mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
}
@@ -69,6 +81,8 @@
out << StringPrintf("Button state: 0x%08x\n", mButtonState);
out << "Down time: " << mDownTime << "\n";
out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n";
+ out << "Is hovering: " << mIsHovering << "\n";
+ out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n";
return out.str();
}
@@ -76,7 +90,7 @@
std::list<NotifyArgs> out;
switch (mCurrentClassification) {
case MotionClassification::TWO_FINGER_SWIPE:
- out.push_back(endScroll(when, when));
+ out += endScroll(when, when);
break;
case MotionClassification::MULTI_FINGER_SWIPE:
out += handleMultiFingerSwipeLift(when, when);
@@ -103,11 +117,11 @@
void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
- // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture
- // is enabled.
- if (std::optional<FloatRect> rect = mPointerController->getBounds(); rect.has_value()) {
- info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, rect->left, rect->right, 0, 0, 0);
- info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, rect->top, rect->bottom, 0, 0, 0);
+ if (!mBoundsInLogicalDisplay.isEmpty()) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left,
+ mBoundsInLogicalDisplay.right, 0, 0, 0);
+ info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, mBoundsInLogicalDisplay.top,
+ mBoundsInLogicalDisplay.bottom, 0, 0, 0);
}
info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0);
@@ -123,6 +137,7 @@
}
std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
+ nsecs_t gestureStartTime,
const Gesture& gesture) {
if (!mDisplayId) {
// Ignore gestures when there is no target display configured.
@@ -131,13 +146,13 @@
switch (gesture.type) {
case kGestureTypeMove:
- return {handleMove(when, readTime, gesture)};
+ return handleMove(when, readTime, gestureStartTime, gesture);
case kGestureTypeButtonsChange:
return handleButtonsChange(when, readTime, gesture);
case kGestureTypeScroll:
return handleScroll(when, readTime, gesture);
case kGestureTypeFling:
- return handleFling(when, readTime, gesture);
+ return handleFling(when, readTime, gestureStartTime, gesture);
case kGestureTypeSwipe:
return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
gesture.details.swipe.dy);
@@ -154,34 +169,66 @@
}
}
-NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
- const Gesture& gesture) {
+std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
+ nsecs_t gestureStartTime,
+ const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
- if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
- enableTapToClick();
+ const auto [oldXCursorPosition, oldYCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
+ if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
+ bool wasHoverCancelled = mIsHoverCancelled;
+ // Gesture will be cancelled if it started before the user started typing and
+ // there is a active IME connection.
+ mIsHoverCancelled = gestureStartTime <= mReaderContext.getLastKeyDownTimestamp() &&
+ mReaderContext.getPolicy()->isInputMethodConnectionActive();
+
+ if (!wasHoverCancelled && mIsHoverCancelled) {
+ // This is the first event of the cancelled gesture, we won't return because we need to
+ // generate a HOVER_EXIT event
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+ return exitHover(when, readTime, oldXCursorPosition, oldYCursorPosition);
+ } else if (mIsHoverCancelled) {
+ return {};
+ }
}
+
rotateDelta(mOrientation, &deltaX, &deltaY);
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- mPointerController->move(deltaX, deltaY);
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ // Update the cursor, and enable tap to click if the gesture is not cancelled
+ if (!mIsHoverCancelled) {
+ // handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click
+ // for this case as subsequent handleButtonsChange may choose to ignore this tap.
+ if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
+ (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
+ enableTapToClick(when);
+ }
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->move(deltaX, deltaY);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ std::list<NotifyArgs> out;
+ const bool down = isPointerDown(mButtonState);
+ if (!down) {
+ out += enterHover(when, readTime, oldXCursorPosition, oldYCursorPosition);
+ }
+ const auto [newXCursorPosition, newYCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, newXCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, newYCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
- const bool down = isPointerDown(mButtonState);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
- return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
- /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition,
- yCursorPosition);
+ out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
+ /*pointerCount=*/1, &coords, newXCursorPosition,
+ newYCursorPosition));
+ return out;
}
std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
@@ -191,7 +238,8 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -200,8 +248,15 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
- if (mReaderContext.isPreventingTouchpadTaps()) {
- enableTapToClick();
+ // V2 palm rejection should override V1
+ if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
+ enableTapToClick(when);
+ if (gesture.details.buttons.is_tap && when <= mWhenToEnableTapToClick) {
+ // return early to prevent this tap
+ return out;
+ }
+ } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
+ enableTapToClick(when);
if (gesture.details.buttons.is_tap) {
// return early to prevent this tap
return out;
@@ -222,16 +277,16 @@
newButtonState |= actionButton;
pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
actionButton, newButtonState,
- /* pointerCount= */ 1, mFingerProps.data(),
- &coords, xCursorPosition, yCursorPosition));
+ /*pointerCount=*/1, &coords, xCursorPosition,
+ yCursorPosition));
}
}
if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
mDownTime = when;
+ out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), &coords, xCursorPosition,
- yCursorPosition));
+ &coords, xCursorPosition, yCursorPosition));
}
out.splice(out.end(), pressEvents);
@@ -247,20 +302,16 @@
newButtonState &= ~actionButton;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
actionButton, newButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), &coords, xCursorPosition,
- yCursorPosition));
+ &coords, xCursorPosition, yCursorPosition));
}
}
if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
- newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
- &coords, xCursorPosition, yCursorPosition));
- // Send a HOVER_MOVE to tell the application that the mouse is hovering again.
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE,
- /*actionButton=*/0, newButtonState, /*pointerCount=*/1,
- mFingerProps.data(), &coords, xCursorPosition,
- yCursorPosition));
+ newButtonState, /* pointerCount= */ 1, &coords,
+ xCursorPosition, yCursorPosition));
+ mButtonState = newButtonState;
+ out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
}
mButtonState = newButtonState;
return out;
@@ -268,7 +319,8 @@
std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
PointerCoords coords;
coords.clear();
@@ -284,18 +336,18 @@
if (mButtonState & button) {
newButtonState &= ~button;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- button, newButtonState, /*pointerCount=*/1,
- mFingerProps.data(), &coords, xCursorPosition,
- yCursorPosition));
+ button, newButtonState, /*pointerCount=*/1, &coords,
+ xCursorPosition, yCursorPosition));
}
}
+ mButtonState = 0;
if (pointerDown) {
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
- newButtonState, /*pointerCount=*/1, mFingerProps.data(),
- &coords, xCursorPosition, yCursorPosition));
+ mButtonState, /*pointerCount=*/1, &coords, xCursorPosition,
+ yCursorPosition));
+ out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
}
- mButtonState = 0;
return out;
}
@@ -303,8 +355,11 @@
const Gesture& gesture) {
std::list<NotifyArgs> out;
PointerCoords& coords = mFakeFingerCoords[0];
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
+ out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+
mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
@@ -312,8 +367,8 @@
mDownTime = when;
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+ mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(),
+ xCursorPosition, yCursorPosition);
args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
}
@@ -328,14 +383,15 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+ mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(),
+ xCursorPosition, yCursorPosition);
args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
return out;
}
std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime,
+ nsecs_t gestureStartTime,
const Gesture& gesture) {
switch (gesture.details.fling.fling_state) {
case GESTURES_FLING_START:
@@ -344,7 +400,7 @@
// ensure consistency between touchscreen and touchpad flings), so we're just using
// the "start fling" gestures as a marker for the end of a two-finger scroll
// gesture.
- return {endScroll(when, readTime)};
+ return endScroll(when, readTime);
}
break;
case GESTURES_FLING_TAP_DOWN:
@@ -354,13 +410,10 @@
// magnitude, which will also result in the pointer icon being updated.
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
// initiated with a touchpad.
- if (!mReaderContext.isPreventingTouchpadTaps()) {
- enableTapToClick();
- }
- return {handleMove(when, readTime,
- Gesture(kGestureMove, gesture.start_time, gesture.end_time,
- /*dx=*/0.f,
- /*dy=*/0.f))};
+ return handleMove(when, readTime, gestureStartTime,
+ Gesture(kGestureMove, gesture.start_time, gesture.end_time,
+ /*dx=*/0.f,
+ /*dy=*/0.f));
}
break;
default:
@@ -370,17 +423,21 @@
return {};
}
-NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition);
+ mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(),
+ xCursorPosition, yCursorPosition);
args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+ out.push_back(args);
mCurrentClassification = MotionClassification::NONE;
- return args;
+ out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ return out;
}
[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
@@ -389,13 +446,18 @@
float dx, float dy) {
std::list<NotifyArgs> out = {};
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
// If the user changes the number of fingers mid-way through a swipe (e.g. they start with
// three and then put a fourth finger down), the gesture library will treat it as two
// separate swipes with an appropriate lift event between them, so we don't have to worry
// about the finger count changing mid-swipe.
+
+ out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+
mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
+
mSwipeFingerCount = fingerCount;
constexpr float FAKE_FINGER_SPACING = 100;
@@ -414,16 +476,14 @@
fingerCount);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
for (size_t i = 1; i < mSwipeFingerCount; i++) {
out.push_back(makeMotionArgs(when, readTime,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
/* actionButton= */ 0, mButtonState,
- /* pointerCount= */ i + 1, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ /* pointerCount= */ i + 1, mFakeFingerCoords.data(),
+ xCursorPosition, yCursorPosition));
}
}
float rotatedDeltaX = dx, rotatedDeltaY = -dy;
@@ -441,8 +501,7 @@
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ mSwipeFingerCount,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
return out;
}
@@ -452,7 +511,8 @@
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
return out;
}
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
@@ -461,22 +521,22 @@
AMOTION_EVENT_ACTION_POINTER_UP |
((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
/* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
}
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0);
mCurrentClassification = MotionClassification::NONE;
+ out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
mSwipeFingerCount = 0;
return out;
}
[[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
// Pinch gesture phases are reported a little differently from others, in that the same details
// struct is used for all phases of the gesture, just with different zoom_state values. When
@@ -487,6 +547,10 @@
LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START,
"First pinch gesture does not have the START zoom state (%d instead).",
gesture.details.pinch.zoom_state);
+ std::list<NotifyArgs> out;
+
+ out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+
mCurrentClassification = MotionClassification::PINCH;
mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX;
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
@@ -499,17 +563,14 @@
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
mDownTime = when;
- std::list<NotifyArgs> out;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
out.push_back(makeMotionArgs(when, readTime,
AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
return out;
}
@@ -527,33 +588,67 @@
xCursorPosition + mPinchFingerSeparation / 2);
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
- mButtonState, /*pointerCount=*/2, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)};
+ mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data(),
+ xCursorPosition, yCursorPosition)};
}
std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+ const auto [xCursorPosition, yCursorPosition] =
+ mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
out.push_back(makeMotionArgs(when, readTime,
AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
/*actionButton=*/0, mButtonState, /*pointerCount=*/2,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
- mButtonState, /*pointerCount=*/1, mFingerProps.data(),
mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
- mCurrentClassification = MotionClassification::NONE;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
+ mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data(),
+ xCursorPosition, yCursorPosition));
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
+ mCurrentClassification = MotionClassification::NONE;
+ out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
return out;
}
+std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime,
+ float xCursorPosition, float yCursorPosition) {
+ if (!mIsHovering) {
+ mIsHovering = true;
+ return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER, xCursorPosition,
+ yCursorPosition)};
+ } else {
+ return {};
+ }
+}
+
+std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime,
+ float xCursorPosition, float yCursorPosition) {
+ if (mIsHovering) {
+ mIsHovering = false;
+ return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT, xCursorPosition,
+ yCursorPosition)};
+ } else {
+ return {};
+ }
+}
+
+NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action,
+ float xCursorPosition, float yCursorPosition) {
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+ return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
+ /*pointerCount=*/1, &coords, xCursorPosition, yCursorPosition);
+}
+
NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
uint32_t pointerCount,
- const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords,
float xCursorPosition, float yCursorPosition) {
return {mReaderContext.getNextId(),
@@ -571,7 +666,7 @@
mCurrentClassification,
AMOTION_EVENT_EDGE_FLAG_NONE,
pointerCount,
- pointerProperties,
+ mFingerProps.data(),
pointerCoords,
/* xPrecision= */ 1.0f,
/* yPrecision= */ 1.0f,
@@ -581,8 +676,11 @@
/* videoFrames= */ {}};
}
-void GestureConverter::enableTapToClick() {
- mReaderContext.setPreventingTouchpadTaps(false);
+void GestureConverter::enableTapToClick(nsecs_t when) {
+ if (mReaderContext.isPreventingTouchpadTaps()) {
+ mWhenToEnableTapToClick = when + TAP_ENABLE_DELAY_NANOS.count();
+ mReaderContext.setPreventingTouchpadTaps(false);
+ }
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index e6cf617..07cc56c 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -34,6 +34,13 @@
namespace android {
+using std::chrono_literals::operator""ms;
+/**
+ * This duration is decided based on internal team testing, it may be updated after testing with
+ * larger groups
+ */
+constexpr std::chrono::nanoseconds TAP_ENABLE_DELAY_NANOS = 400ms;
+
// Converts Gesture structs from the gestures library into NotifyArgs and the appropriate
// PointerController calls.
class GestureConverter {
@@ -48,22 +55,27 @@
void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; }
+ void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; }
+
void populateMotionRanges(InputDeviceInfo& info) const;
[[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
+ nsecs_t gestureStartTime,
const Gesture& gesture);
private:
- [[nodiscard]] NotifyMotionArgs handleMove(nsecs_t when, nsecs_t readTime,
- const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleMove(nsecs_t when, nsecs_t readTime,
+ nsecs_t gestureStartTime,
+ const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime,
+ nsecs_t gestureStartTime,
const Gesture& gesture);
- [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> endScroll(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
uint32_t fingerCount, float dx,
@@ -73,20 +85,30 @@
const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime,
+ float xCursorPosition, float yCursorPosition);
+ [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime,
+ float xCursorPosition, float yCursorPosition);
+
+ NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action,
+ float xCursorPosition, float yCursorPosition);
+
NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
- uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, float xCursorPosition,
- float yCursorPosition);
+ uint32_t pointerCount, const PointerCoords* pointerCoords,
+ float xCursorPosition, float yCursorPosition);
- void enableTapToClick();
+ void enableTapToClick(nsecs_t when);
+ bool mIsHoverCancelled{false};
+ nsecs_t mWhenToEnableTapToClick{0};
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
std::shared_ptr<PointerControllerInterface> mPointerController;
+ const bool mEnablePointerChoreographer;
std::optional<int32_t> mDisplayId;
+ FloatRect mBoundsInLogicalDisplay{};
ui::Rotation mOrientation = ui::ROTATION_0;
RawAbsoluteAxisInfo mXAxisInfo;
RawAbsoluteAxisInfo mYAxisInfo;
@@ -95,6 +117,9 @@
// button values (AMOTION_EVENT_BUTTON_...).
uint32_t mButtonState = 0;
nsecs_t mDownTime = 0;
+ // Whether we are currently in a hover state (i.e. a HOVER_ENTER event has been sent without a
+ // matching HOVER_EXIT).
+ bool mIsHovering = false;
MotionClassification mCurrentClassification = MotionClassification::NONE;
// Only used when mCurrentClassification is MULTI_FINGER_SWIPE.
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 6780dce..b89b7f3 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -22,10 +22,15 @@
#include <chrono>
#include <vector>
+#include <com_android_input_flags.h>
#include <linux/input-event-codes.h>
+namespace input_flags = com::android::input::flags;
+
namespace android {
+const bool REPORT_PALMS_TO_GESTURES_LIBRARY = input_flags::report_palms_to_gestures_library();
+
HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext,
MultiTouchMotionAccumulator& motionAccumulator)
: mDeviceContext(deviceContext),
@@ -84,7 +89,7 @@
}
// Some touchpads continue to report contacts even after they've identified them as palms.
// We want to exclude these contacts from the HardwareStates.
- if (slot.getToolType() == ToolType::PALM) {
+ if (!REPORT_PALMS_TO_GESTURES_LIBRARY && slot.getToolType() == ToolType::PALM) {
numPalms++;
continue;
}
@@ -100,6 +105,11 @@
fingerState.position_x = slot.getX();
fingerState.position_y = slot.getY();
fingerState.tracking_id = slot.getTrackingId();
+ if (REPORT_PALMS_TO_GESTURES_LIBRARY) {
+ fingerState.tool_type = slot.getToolType() == ToolType::PALM
+ ? FingerState::ToolType::kPalm
+ : FingerState::ToolType::kFinger;
+ }
}
schs.state.fingers = schs.fingers.data();
schs.state.finger_cnt = schs.fingers.size();
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/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 23c1691..2803805 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -31,13 +31,14 @@
out: ["inputflinger_bootstrap.rs.h"],
}
-rust_ffi_static {
- name: "libinputflinger_rs",
+rust_defaults {
+ name: "libinputflinger_rs_defaults",
crate_name: "inputflinger",
srcs: ["lib.rs"],
rustlibs: [
"libcxx",
"com.android.server.inputflinger-rust",
+ "android.hardware.input.common-V1-rust",
"libbinder_rs",
"liblog_rust",
"liblogger",
@@ -45,6 +46,24 @@
host_supported: true,
}
+rust_ffi_static {
+ name: "libinputflinger_rs",
+ defaults: ["libinputflinger_rs_defaults"],
+}
+
+rust_test {
+ name: "libinputflinger_rs_test",
+ defaults: ["libinputflinger_rs_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+ test_suites: ["device_tests"],
+ sanitize: {
+ address: true,
+ hwaddress: true,
+ },
+}
+
cc_library_headers {
name: "inputflinger_rs_bootstrap_cxx_headers",
host_supported: true,
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
new file mode 100644
index 0000000..894b881
--- /dev/null
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+//! Bounce keys input filter implementation.
+//! Bounce keys is an accessibility feature to aid users who have physical disabilities, that
+//! allows the user to configure the device to ignore rapid, repeated key presses of the same key.
+use crate::input_filter::Filter;
+
+use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use log::debug;
+use std::collections::{HashMap, HashSet};
+
+#[derive(Debug)]
+struct LastUpKeyEvent {
+ keycode: i32,
+ event_time: i64,
+}
+
+#[derive(Debug)]
+struct BlockedEvent {
+ device_id: i32,
+ keycode: i32,
+}
+
+pub struct BounceKeysFilter {
+ next: Box<dyn Filter + Send + Sync>,
+ key_event_map: HashMap<i32, LastUpKeyEvent>,
+ blocked_events: Vec<BlockedEvent>,
+ external_devices: HashSet<i32>,
+ bounce_key_threshold_ns: i64,
+}
+
+impl BounceKeysFilter {
+ /// Create a new BounceKeysFilter instance.
+ pub fn new(
+ next: Box<dyn Filter + Send + Sync>,
+ bounce_key_threshold_ns: i64,
+ ) -> BounceKeysFilter {
+ Self {
+ next,
+ key_event_map: HashMap::new(),
+ blocked_events: Vec::new(),
+ external_devices: HashSet::new(),
+ bounce_key_threshold_ns,
+ }
+ }
+}
+
+impl Filter for BounceKeysFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
+ self.next.notify_key(event);
+ return;
+ }
+ match event.action {
+ KeyEventAction::DOWN => match self.key_event_map.get(&event.deviceId) {
+ None => self.next.notify_key(event),
+ Some(last_up_event) => {
+ if event.keyCode == last_up_event.keycode
+ && event.eventTime < last_up_event.event_time + self.bounce_key_threshold_ns
+ {
+ self.blocked_events.push(BlockedEvent {
+ device_id: event.deviceId,
+ keycode: event.keyCode,
+ });
+ debug!("Event dropped because last up was too recent");
+ } else {
+ self.key_event_map.remove(&event.deviceId);
+ self.next.notify_key(event);
+ }
+ }
+ },
+ KeyEventAction::UP => {
+ self.key_event_map.insert(
+ event.deviceId,
+ LastUpKeyEvent { keycode: event.keyCode, event_time: event.eventTime },
+ );
+ if let Some(index) = self.blocked_events.iter().position(|blocked_event| {
+ blocked_event.device_id == event.deviceId
+ && blocked_event.keycode == event.keyCode
+ }) {
+ self.blocked_events.remove(index);
+ debug!("Event dropped because key down was already dropped");
+ } else {
+ self.next.notify_key(event);
+ }
+ }
+ _ => (),
+ }
+ }
+
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+ self.key_event_map.retain(|id, _| device_infos.iter().any(|x| *id == x.deviceId));
+ self.blocked_events.retain(|blocked_event| {
+ device_infos.iter().any(|x| blocked_event.device_id == x.deviceId)
+ });
+ self.external_devices.clear();
+ for device_info in device_infos {
+ if device_info.external {
+ self.external_devices.insert(device_info.deviceId);
+ }
+ }
+ self.next.notify_devices_changed(device_infos);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::bounce_keys_filter::BounceKeysFilter;
+ use crate::input_filter::{test_filter::TestFilter, Filter};
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+ };
+
+ static BASE_KEY_EVENT: KeyEvent = KeyEvent {
+ id: 1,
+ deviceId: 1,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 1,
+ scanCode: 0,
+ metaState: 0,
+ };
+
+ #[test]
+ fn test_is_notify_key_for_external_keyboard() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_doesnt_block_for_internal_keyboard() {
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_internal_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_doesnt_block_for_external_stylus() {
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ );
+
+ let event =
+ KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event =
+ KeyEvent { action: KeyEventAction::UP, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event =
+ KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_is_notify_key_for_multiple_external_keyboards() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_devices(
+ Box::new(next.clone()),
+ &[
+ DeviceInfo { deviceId: 1, external: true },
+ DeviceInfo { deviceId: 2, external: true },
+ ],
+ 100, /* threshold */
+ );
+
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ fn setup_filter_with_external_device(
+ next: Box<dyn Filter + Send + Sync>,
+ device_id: i32,
+ threshold: i64,
+ ) -> BounceKeysFilter {
+ setup_filter_with_devices(
+ next,
+ &[DeviceInfo { deviceId: device_id, external: true }],
+ threshold,
+ )
+ }
+
+ fn setup_filter_with_internal_device(
+ next: Box<dyn Filter + Send + Sync>,
+ device_id: i32,
+ threshold: i64,
+ ) -> BounceKeysFilter {
+ setup_filter_with_devices(
+ next,
+ &[DeviceInfo { deviceId: device_id, external: false }],
+ threshold,
+ )
+ }
+
+ fn setup_filter_with_devices(
+ next: Box<dyn Filter + Send + Sync>,
+ devices: &[DeviceInfo],
+ threshold: i64,
+ ) -> BounceKeysFilter {
+ let mut filter = BounceKeysFilter::new(next, threshold);
+ filter.notify_devices_changed(devices);
+ filter
+ }
+}
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
new file mode 100644
index 0000000..e94a71f
--- /dev/null
+++ b/services/inputflinger/rust/input_filter.rs
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ */
+
+//! InputFilter manages all the filtering components that can intercept events, modify the events,
+//! block events, etc depending on the situation. This will be used support Accessibility features
+//! like Sticky keys, Slow keys, Bounce keys, etc.
+
+use binder::{Interface, Strong};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo,
+ IInputFilter::{IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ InputFilterConfiguration::InputFilterConfiguration,
+ KeyEvent::KeyEvent,
+};
+
+use crate::bounce_keys_filter::BounceKeysFilter;
+use crate::sticky_keys_filter::StickyKeysFilter;
+use log::{error, info};
+use std::sync::{Arc, Mutex, RwLock};
+
+/// Interface for all the sub input filters
+pub trait Filter {
+ fn notify_key(&mut self, event: &KeyEvent);
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]);
+}
+
+struct InputFilterState {
+ first_filter: Box<dyn Filter + Send + Sync>,
+ enabled: bool,
+}
+
+/// The rust implementation of InputFilter
+pub struct InputFilter {
+ // In order to have multiple immutable references to the callbacks that is thread safe need to
+ // wrap the callbacks in Arc<RwLock<...>>
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ // Access to mutable references to mutable state (includes access to filters, enabled, etc.) is
+ // guarded by Mutex for thread safety
+ state: Mutex<InputFilterState>,
+}
+
+impl Interface for InputFilter {}
+
+impl InputFilter {
+ /// Create a new InputFilter instance.
+ pub fn new(callbacks: Strong<dyn IInputFilterCallbacks>) -> InputFilter {
+ let ref_callbacks = Arc::new(RwLock::new(callbacks));
+ let base_filter = Box::new(BaseFilter::new(ref_callbacks.clone()));
+ Self::create_input_filter(base_filter, ref_callbacks)
+ }
+
+ /// Create test instance of InputFilter
+ fn create_input_filter(
+ first_filter: Box<dyn Filter + Send + Sync>,
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ ) -> InputFilter {
+ Self { callbacks, state: Mutex::new(InputFilterState { first_filter, enabled: false }) }
+ }
+}
+
+impl IInputFilter for InputFilter {
+ fn isEnabled(&self) -> binder::Result<bool> {
+ Result::Ok(self.state.lock().unwrap().enabled)
+ }
+
+ fn notifyKey(&self, event: &KeyEvent) -> binder::Result<()> {
+ let first_filter = &mut self.state.lock().unwrap().first_filter;
+ first_filter.notify_key(event);
+ Result::Ok(())
+ }
+
+ fn notifyInputDevicesChanged(&self, device_infos: &[DeviceInfo]) -> binder::Result<()> {
+ let first_filter = &mut self.state.lock().unwrap().first_filter;
+ first_filter.notify_devices_changed(device_infos);
+ Result::Ok(())
+ }
+
+ fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> {
+ let mut state = self.state.lock().unwrap();
+ let mut first_filter: Box<dyn Filter + Send + Sync> =
+ Box::new(BaseFilter::new(self.callbacks.clone()));
+ if config.stickyKeysEnabled {
+ first_filter = Box::new(StickyKeysFilter::new(
+ first_filter,
+ ModifierStateListener::new(self.callbacks.clone()),
+ ));
+ state.enabled = true;
+ info!("Sticky keys filter is installed");
+ }
+ if config.bounceKeysThresholdNs > 0 {
+ first_filter =
+ Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
+ state.enabled = true;
+ info!("Bounce keys filter is installed");
+ }
+ state.first_filter = first_filter;
+ Result::Ok(())
+ }
+}
+
+struct BaseFilter {
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+}
+
+impl BaseFilter {
+ fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> BaseFilter {
+ Self { callbacks }
+ }
+}
+
+impl Filter for BaseFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ match self.callbacks.read().unwrap().sendKeyEvent(event) {
+ Ok(_) => (),
+ _ => error!("Failed to send key event back to native C++"),
+ }
+ }
+
+ fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
+ // do nothing
+ }
+}
+
+pub struct ModifierStateListener {
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+}
+
+impl ModifierStateListener {
+ /// Create a new InputFilter instance.
+ pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener {
+ Self { callbacks }
+ }
+
+ pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) {
+ let _ = self
+ .callbacks
+ .read()
+ .unwrap()
+ .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter,
+ };
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use binder::Strong;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, IInputFilter::IInputFilter,
+ InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
+ KeyEventAction::KeyEventAction,
+ };
+ use std::sync::{Arc, RwLock};
+
+ #[test]
+ fn test_not_enabled_with_default_filter() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(!result.unwrap());
+ }
+
+ #[test]
+ fn test_notify_key_with_no_filters() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone())));
+ let event = create_key_event();
+ assert!(input_filter.notifyKey(&event).is_ok());
+ assert_eq!(test_callbacks.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_notify_key_with_filter() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
+ let event = create_key_event();
+ assert!(input_filter.notifyKey(&event).is_ok());
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_notify_devices_changed() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
+ assert!(input_filter
+ .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }])
+ .is_ok());
+ assert!(test_filter.is_device_changed_called());
+ }
+
+ #[test]
+ fn test_notify_configuration_changed_enabled_bounce_keys() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ bounceKeysThresholdNs: 100,
+ stickyKeysEnabled: false,
+ });
+ assert!(result.is_ok());
+ let result = input_filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(result.unwrap());
+ }
+
+ #[test]
+ fn test_notify_configuration_changed_enabled_sticky_keys() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ bounceKeysThresholdNs: 0,
+ stickyKeysEnabled: true,
+ });
+ assert!(result.is_ok());
+ let result = input_filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(result.unwrap());
+ }
+
+ fn create_key_event() -> KeyEvent {
+ KeyEvent {
+ id: 1,
+ deviceId: 1,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 0,
+ scanCode: 0,
+ metaState: 0,
+ }
+ }
+}
+
+#[cfg(test)]
+pub mod test_filter {
+ use crate::input_filter::Filter;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent,
+ };
+ use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+ #[derive(Default)]
+ struct TestFilterInner {
+ is_device_changed_called: bool,
+ last_event: Option<KeyEvent>,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestFilter(Arc<RwLock<TestFilterInner>>);
+
+ impl TestFilter {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&mut self) -> RwLockWriteGuard<'_, TestFilterInner> {
+ self.0.write().unwrap()
+ }
+
+ pub fn last_event(&self) -> Option<KeyEvent> {
+ self.0.read().unwrap().last_event
+ }
+
+ pub fn clear(&mut self) {
+ self.inner().last_event = None
+ }
+
+ pub fn is_device_changed_called(&self) -> bool {
+ self.0.read().unwrap().is_device_changed_called
+ }
+ }
+
+ impl Filter for TestFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ self.inner().last_event = Some(*event);
+ }
+ fn notify_devices_changed(&mut self, _device_infos: &[DeviceInfo]) {
+ self.inner().is_device_changed_called = true;
+ }
+ }
+}
+
+#[cfg(test)]
+pub mod test_callbacks {
+ use binder::Interface;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent,
+ };
+ use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+ #[derive(Default)]
+ struct TestCallbacksInner {
+ last_modifier_state: u32,
+ last_locked_modifier_state: u32,
+ last_event: Option<KeyEvent>,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestCallbacks(Arc<RwLock<TestCallbacksInner>>);
+
+ impl Interface for TestCallbacks {}
+
+ impl TestCallbacks {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&self) -> RwLockWriteGuard<'_, TestCallbacksInner> {
+ self.0.write().unwrap()
+ }
+
+ pub fn last_event(&self) -> Option<KeyEvent> {
+ self.0.read().unwrap().last_event
+ }
+
+ pub fn clear(&mut self) {
+ self.inner().last_event = None;
+ self.inner().last_modifier_state = 0;
+ self.inner().last_locked_modifier_state = 0;
+ }
+
+ pub fn get_last_modifier_state(&self) -> u32 {
+ self.0.read().unwrap().last_modifier_state
+ }
+
+ pub fn get_last_locked_modifier_state(&self) -> u32 {
+ self.0.read().unwrap().last_locked_modifier_state
+ }
+ }
+
+ impl IInputFilterCallbacks for TestCallbacks {
+ fn sendKeyEvent(&self, event: &KeyEvent) -> binder::Result<()> {
+ self.inner().last_event = Some(*event);
+ Result::Ok(())
+ }
+
+ fn onModifierStateChanged(
+ &self,
+ modifier_state: i32,
+ locked_modifier_state: i32,
+ ) -> std::result::Result<(), binder::Status> {
+ self.inner().last_modifier_state = modifier_state as u32;
+ self.inner().last_locked_modifier_state = locked_modifier_state as u32;
+ Result::Ok(())
+ }
+ }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index 501e435..fa16898 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -19,13 +19,21 @@
//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
//! pass it back to C++ as a local AIDL interface.
+mod bounce_keys_filter;
+mod input_filter;
+mod sticky_keys_filter;
+
+use crate::input_filter::InputFilter;
use binder::{
- unstable_api::{AIBinder, new_spibinder,},
+ unstable_api::{new_spibinder, AIBinder},
BinderFeatures, Interface, StatusCode, Strong,
};
-use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputFlingerRust::{
- BnInputFlingerRust, IInputFlingerRust,
- IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::{BnInputFilter, IInputFilter, IInputFilterCallbacks::IInputFilterCallbacks},
+ IInputFlingerRust::{
+ BnInputFlingerRust, IInputFlingerRust,
+ IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+ },
};
use log::debug;
@@ -71,8 +79,8 @@
// SAFETY: Our caller guaranteed that `callback` is a valid pointer to an `AIBinder` and its
// reference count has been incremented..
let Some(callback) = (unsafe { new_spibinder(callback) }) else {
- panic!("Failed to get SpAIBinder from raw callback pointer");
- };
+ panic!("Failed to get SpAIBinder from raw callback pointer");
+ };
let callback: Result<Strong<dyn IInputFlingerRustBootstrapCallback>, StatusCode> =
callback.into_interface();
@@ -93,7 +101,19 @@
impl Interface for InputFlingerRust {}
-impl IInputFlingerRust for InputFlingerRust {}
+impl IInputFlingerRust for InputFlingerRust {
+ fn createInputFilter(
+ &self,
+ callbacks: &Strong<dyn IInputFilterCallbacks>,
+ ) -> binder::Result<Strong<dyn IInputFilter>> {
+ debug!("Creating InputFilter");
+ let filter = BnInputFilter::new_binder(
+ InputFilter::new(callbacks.clone()),
+ BinderFeatures::default(),
+ );
+ Result::Ok(filter)
+ }
+}
impl Drop for InputFlingerRust {
fn drop(&mut self) {
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
new file mode 100644
index 0000000..da581b8
--- /dev/null
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -0,0 +1,515 @@
+/*
+ * 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.
+ */
+
+//! Sticky keys input filter implementation.
+//! Sticky keys is an accessibility feature that assists users who have physical disabilities or
+//! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing
+//! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift,
+//! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed.
+use crate::input_filter::{Filter, ModifierStateListener};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use std::collections::HashSet;
+
+// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
+const KEYCODE_ALT_LEFT: i32 = 57;
+const KEYCODE_ALT_RIGHT: i32 = 58;
+const KEYCODE_SHIFT_LEFT: i32 = 59;
+const KEYCODE_SHIFT_RIGHT: i32 = 60;
+const KEYCODE_SYM: i32 = 63;
+const KEYCODE_CTRL_LEFT: i32 = 113;
+const KEYCODE_CTRL_RIGHT: i32 = 114;
+const KEYCODE_CAPS_LOCK: i32 = 115;
+const KEYCODE_SCROLL_LOCK: i32 = 116;
+const KEYCODE_META_LEFT: i32 = 117;
+const KEYCODE_META_RIGHT: i32 = 118;
+const KEYCODE_FUNCTION: i32 = 119;
+const KEYCODE_NUM_LOCK: i32 = 143;
+
+// Modifier states: values are from /frameworks/native/include/android/input.h
+const META_ALT_ON: u32 = 0x02;
+const META_ALT_LEFT_ON: u32 = 0x10;
+const META_ALT_RIGHT_ON: u32 = 0x20;
+const META_SHIFT_ON: u32 = 0x01;
+const META_SHIFT_LEFT_ON: u32 = 0x40;
+const META_SHIFT_RIGHT_ON: u32 = 0x80;
+const META_CTRL_ON: u32 = 0x1000;
+const META_CTRL_LEFT_ON: u32 = 0x2000;
+const META_CTRL_RIGHT_ON: u32 = 0x4000;
+const META_META_ON: u32 = 0x10000;
+const META_META_LEFT_ON: u32 = 0x20000;
+const META_META_RIGHT_ON: u32 = 0x40000;
+
+pub struct StickyKeysFilter {
+ next: Box<dyn Filter + Send + Sync>,
+ listener: ModifierStateListener,
+ /// Tracking devices that contributed to the modifier state.
+ contributing_devices: HashSet<i32>,
+ /// State describing the current enabled modifiers. This contain both locked and non-locked
+ /// modifier state bits.
+ modifier_state: u32,
+ /// State describing the current locked modifiers. These modifiers will not be cleared on a
+ /// non-modifier key press. They will be cleared only if the locked modifier key is pressed
+ /// again.
+ locked_modifier_state: u32,
+}
+
+impl StickyKeysFilter {
+ /// Create a new StickyKeysFilter instance.
+ pub fn new(
+ next: Box<dyn Filter + Send + Sync>,
+ listener: ModifierStateListener,
+ ) -> StickyKeysFilter {
+ Self {
+ next,
+ listener,
+ contributing_devices: HashSet::new(),
+ modifier_state: 0,
+ locked_modifier_state: 0,
+ }
+ }
+}
+
+impl Filter for StickyKeysFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ let up = event.action == KeyEventAction::UP;
+ let mut modifier_state = self.modifier_state;
+ let mut locked_modifier_state = self.locked_modifier_state;
+ if !is_ephemeral_modifier_key(event.keyCode) {
+ // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
+ // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
+ // the KeyEvent.
+ let old_modifier_state = event.metaState as u32;
+ let mut new_event = *event;
+ // Send the current modifier state with the key event before clearing non-locked
+ // modifier state
+ new_event.metaState =
+ (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32;
+ self.next.notify_key(&new_event);
+ if up && !is_modifier_key(event.keyCode) {
+ modifier_state =
+ clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state;
+ }
+ } else if up {
+ // Update contributing devices to track keyboards
+ self.contributing_devices.insert(event.deviceId);
+ // If ephemeral modifier key, capture the key and update the sticky modifier states
+ let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
+ let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
+ if locked_modifier_state & modifier_key_mask != 0 {
+ locked_modifier_state &= !symmetrical_modifier_key_mask;
+ modifier_state &= !symmetrical_modifier_key_mask;
+ } else if modifier_key_mask & modifier_state != 0 {
+ locked_modifier_state |= modifier_key_mask;
+ modifier_state =
+ (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask;
+ } else {
+ modifier_state |= modifier_key_mask;
+ }
+ }
+ if self.modifier_state != modifier_state
+ || self.locked_modifier_state != locked_modifier_state
+ {
+ self.modifier_state = modifier_state;
+ self.locked_modifier_state = locked_modifier_state;
+ self.listener.modifier_state_changed(modifier_state, locked_modifier_state);
+ }
+ }
+
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+ // Clear state if all contributing devices removed
+ self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
+ if self.contributing_devices.is_empty()
+ && (self.modifier_state != 0 || self.locked_modifier_state != 0)
+ {
+ self.modifier_state = 0;
+ self.locked_modifier_state = 0;
+ self.listener.modifier_state_changed(0, 0);
+ }
+ self.next.notify_devices_changed(device_infos);
+ }
+}
+
+fn is_modifier_key(keycode: i32) -> bool {
+ matches!(
+ keycode,
+ KEYCODE_ALT_LEFT
+ | KEYCODE_ALT_RIGHT
+ | KEYCODE_SHIFT_LEFT
+ | KEYCODE_SHIFT_RIGHT
+ | KEYCODE_CTRL_LEFT
+ | KEYCODE_CTRL_RIGHT
+ | KEYCODE_META_LEFT
+ | KEYCODE_META_RIGHT
+ | KEYCODE_SYM
+ | KEYCODE_FUNCTION
+ | KEYCODE_CAPS_LOCK
+ | KEYCODE_NUM_LOCK
+ | KEYCODE_SCROLL_LOCK
+ )
+}
+
+fn is_ephemeral_modifier_key(keycode: i32) -> bool {
+ matches!(
+ keycode,
+ KEYCODE_ALT_LEFT
+ | KEYCODE_ALT_RIGHT
+ | KEYCODE_SHIFT_LEFT
+ | KEYCODE_SHIFT_RIGHT
+ | KEYCODE_CTRL_LEFT
+ | KEYCODE_CTRL_RIGHT
+ | KEYCODE_META_LEFT
+ | KEYCODE_META_RIGHT
+ )
+}
+
+fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 {
+ match keycode {
+ KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON,
+ KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON,
+ KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON,
+ KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON,
+ KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON,
+ KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON,
+ KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON,
+ _ => 0,
+ }
+}
+
+/// Modifier mask including both left and right versions of a modifier key.
+fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 {
+ match keycode {
+ KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => {
+ META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON
+ }
+ KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => {
+ META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON
+ }
+ KEYCODE_META_LEFT | KEYCODE_META_RIGHT => {
+ META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON
+ }
+ _ => 0,
+ }
+}
+
+fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 {
+ modifier_state
+ & !(META_ALT_LEFT_ON
+ | META_ALT_RIGHT_ON
+ | META_ALT_ON
+ | META_SHIFT_LEFT_ON
+ | META_SHIFT_RIGHT_ON
+ | META_SHIFT_ON
+ | META_CTRL_LEFT_ON
+ | META_CTRL_RIGHT_ON
+ | META_CTRL_ON
+ | META_META_LEFT_ON
+ | META_META_RIGHT_ON
+ | META_META_ON)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener,
+ };
+ use crate::sticky_keys_filter::{
+ StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK,
+ KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON,
+ META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON,
+ META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON,
+ };
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use binder::Strong;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+ KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+ };
+ use std::sync::{Arc, RwLock};
+
+ static DEVICE_ID: i32 = 1;
+ static KEY_A: i32 = 29;
+ static BASE_KEY_DOWN: KeyEvent = KeyEvent {
+ id: 1,
+ deviceId: DEVICE_ID,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 0,
+ scanCode: 0,
+ metaState: 0,
+ };
+
+ static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN };
+
+ #[test]
+ fn test_notify_key_consumes_ephemeral_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_ALT_LEFT,
+ KEYCODE_ALT_RIGHT,
+ KEYCODE_CTRL_LEFT,
+ KEYCODE_CTRL_RIGHT,
+ KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT,
+ KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT,
+ ];
+ for key_code in key_codes.iter() {
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN });
+ assert!(test_filter.last_event().is_none());
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP });
+ assert!(test_filter.last_event().is_none());
+ }
+ }
+
+ #[test]
+ fn test_notify_key_passes_non_ephemeral_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_CAPS_LOCK,
+ KEYCODE_NUM_LOCK,
+ KEYCODE_SCROLL_LOCK,
+ KEYCODE_FUNCTION,
+ KEYCODE_SYM,
+ ];
+ for key_code in key_codes.iter() {
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+ }
+
+ #[test]
+ fn test_notify_key_passes_non_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+
+ let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_modifier_state_updated_on_modifier_key_press() {
+ let mut test_filter = TestFilter::new();
+ let mut test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let test_states = &[
+ (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON),
+ (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON),
+ (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON),
+ (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON),
+ (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON),
+ (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON),
+ (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON),
+ (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON),
+ ];
+ for test_state in test_states.iter() {
+ test_filter.clear();
+ test_callbacks.clear();
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ // Re-send keys to lock it
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
+
+ // Re-send keys to clear
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+ }
+
+ #[test]
+ fn test_modifier_state_cleared_on_non_modifier_key_press() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+
+ #[test]
+ fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP });
+
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ }
+
+ #[test]
+ fn test_key_events_have_sticky_modifier_state() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ assert_eq!(
+ test_filter.last_event().unwrap().metaState as u32,
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+ assert_eq!(
+ test_filter.last_event().unwrap().metaState as u32,
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ }
+
+ #[test]
+ fn test_modifier_state_not_cleared_until_all_devices_removed() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 1,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_DOWN
+ });
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 1,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_UP
+ });
+
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 2,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_DOWN
+ });
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 2,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_UP
+ });
+
+ sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_devices_changed(&[]);
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+
+ fn setup_filter(
+ next: Box<dyn Filter + Send + Sync>,
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ ) -> StickyKeysFilter {
+ StickyKeysFilter::new(next, ModifierStateListener::new(callbacks))
+ }
+}
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/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index b55c9cc..f3a6f01 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -16,13 +16,24 @@
#include "CursorInputMapper.h"
+#include <list>
+#include <string>
+#include <tuple>
+#include <variant>
+
#include <android-base/logging.h>
+#include <com_android_input_flags.h>
#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+#include <linux/input.h>
+#include <utils/Timers.h>
#include "FakePointerController.h"
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
+#include "NotifyArgs.h"
#include "TestEventMatchers.h"
+#include "ui/Rotation.h"
#define TAG "CursorInputMapper_test"
@@ -38,18 +49,30 @@
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+constexpr int32_t DISPLAY_ID = 0;
+constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+
+constexpr int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+namespace input_flags = com::android::input::flags;
/**
* Unit tests for CursorInputMapper.
- * This class is named 'CursorInputMapperUnitTest' to avoid name collision with the existing
- * 'CursorInputMapperTest'. If all of the CursorInputMapper tests are migrated here, the name
- * can be simplified to 'CursorInputMapperTest'.
- * TODO(b/283812079): move CursorInputMapper tests here.
+ * These classes are named 'CursorInputMapperUnitTest...' to avoid name collision with the existing
+ * 'CursorInputMapperTest...' classes. If all of the CursorInputMapper tests are migrated here, the
+ * name can be simplified to 'CursorInputMapperTest'.
+ *
+ * TODO(b/283812079): move the remaining CursorInputMapper tests here. The ones that are left all
+ * depend on viewport association, for which we'll need to fake InputDeviceContext.
*/
-class CursorInputMapperUnitTest : public InputMapperUnitTest {
+class CursorInputMapperUnitTestBase : public InputMapperUnitTest {
protected:
- void SetUp() override {
- InputMapperUnitTest::SetUp();
+ void SetUp() override { SetUpWithBus(BUS_USB); }
+ void SetUpWithBus(int bus) override {
+ InputMapperUnitTest::SetUpWithBus(bus);
// Current scan code state - all keys are UP by default
setScanCodeState(KeyState::UP,
@@ -60,6 +83,14 @@
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL))
.WillRepeatedly(Return(false));
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
+ }
+
+ void createMapper() {
+ createDevice();
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
}
@@ -71,19 +102,42 @@
mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::POINTER_CAPTURE);
ASSERT_THAT(args,
- ElementsAre(
- VariantWith<NotifyDeviceResetArgs>(AllOf(WithDeviceId(DEVICE_ID)))));
+ ElementsAre(VariantWith<NotifyDeviceResetArgs>(
+ AllOf(WithDeviceId(DEVICE_ID), WithEventTime(ARBITRARY_TIME)))));
// Check that generation also got bumped
ASSERT_GT(mDevice->getGeneration(), generation);
}
};
+class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ CursorInputMapperUnitTestBase::SetUp();
+ }
+};
+
+TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mMapper->getSources());
+}
+
+TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsTrackballInNavigationMode) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mMapper->getSources());
+}
+
/**
* Move the mouse and then click the button. Check whether HOVER_EXIT is generated when hovering
* ends. Currently, it is not.
*/
TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) {
+ createMapper();
std::list<NotifyArgs> args;
// Move the cursor a little
@@ -127,6 +181,7 @@
* When it's not SOURCE_MOUSE, CursorInputMapper doesn't populate cursor position values.
*/
TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) {
+ createMapper();
setPointerCapture(true);
std::list<NotifyArgs> args;
@@ -139,6 +194,7 @@
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(ACTION_MOVE),
WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), WithCoords(10.0f, 20.0f),
+ WithRelativeMotion(10.0f, 20.0f),
WithCursorPosition(INVALID_CURSOR_POSITION,
INVALID_CURSOR_POSITION)))));
@@ -178,12 +234,17 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(ACTION_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithCoords(30.0f, 40.0f)))));
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), WithCoords(30.0f, 40.0f),
+ WithRelativeMotion(30.0f, 40.0f)))));
// Disable pointer capture. Afterwards, events should be generated the usual way.
setPointerCapture(false);
-
+ const auto expectedCoords = input_flags::enable_pointer_choreographer()
+ ? WithCoords(0, 0)
+ : WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
+ const auto expectedCursorPosition = input_flags::enable_pointer_choreographer()
+ ? WithCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION)
+ : WithCursorPosition(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
args.clear();
args += process(EV_REL, REL_X, 10);
args += process(EV_REL, REL_Y, 20);
@@ -191,9 +252,982 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
- WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f),
- WithCursorPosition(INITIAL_CURSOR_X + 10.0f,
- INITIAL_CURSOR_Y + 20.0f)))));
+ expectedCoords, expectedCursorPosition,
+ WithRelativeMotion(10.0f, 20.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest,
+ PopulateDeviceInfoReturnsRangeFromPointerControllerInPointerMode) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ mFakePolicy->clearViewports();
+ mFakePointerController->clearBounds();
+ createMapper();
+
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ // Initially there should not be a valid motion range because there's no viewport or pointer
+ // bounds.
+ 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 bounds are set, then there should be a valid motion range.
+ mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(systemTime(), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ InputDeviceInfo info2;
+ mMapper->populateDeviceInfo(info2);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 1,
+ 800 - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 2,
+ 480 - 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(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
+ -1.0f, 1.0f, 0.0f,
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL,
+ -1.0f, 1.0f, 0.0f,
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_TRACKBALL, 0.0f, 1.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldSetAllFieldsAndIncludeGlobalMetaState) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ EXPECT_CALL(mMockInputReaderContext, getGlobalMetaState())
+ .WillRepeatedly(Return(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON));
+
+ std::list<NotifyArgs> args;
+
+ // Button press.
+ // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPointerCount(1), WithPointerId(0, 0),
+ WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
+ WithPressure(1.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPointerCount(1), WithPointerId(0, 0),
+ WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
+ WithPressure(1.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME)))));
+ args.clear();
+
+ // Button release. Should have same down time.
+ args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
+ args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME + 1),
+ WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(0), WithPointerCount(1),
+ WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME + 1),
+ WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(0), WithPointerCount(1),
+ WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Motion in X but not Y.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f),
+ WithPressure(0.0f)))));
+ args.clear();
+
+ // Motion in Y but not X.
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(0.0f)))));
+ args.clear();
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentButtonUpdates) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Button press.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ // Button release.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Combined X, Y and Button.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2);
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(1.0f)))));
+ args.clear();
+
+ // Move X, Y a bit while pressed.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 2);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(1.0f)))));
+ args.clear();
+
+ // Release Button.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ args.clear();
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtons) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ // press BTN_LEFT, release BTN_LEFT
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f)))));
+ args.clear();
+
+ // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f)))));
+}
+
+class CursorInputMapperButtonKeyTest
+ : public CursorInputMapperUnitTest,
+ public testing::WithParamInterface<
+ std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
+ int32_t /*expectedKeyCode*/>> {};
+
+TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKey) {
+ auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+ WithKeyCode(expectedKeyCode))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(expectedButtonState),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(expectedButtonState),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
+ WithKeyCode(expectedKeyCode)))));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ SideExtraBackAndForward, CursorInputMapperButtonKeyTest,
+ testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD),
+ std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD,
+ AKEYCODE_FORWARD)));
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldMoveThePointerAroundInPointerMode) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(110.0f, 220.0f), WithPressure(0.0f), WithSize(0.0f),
+ WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
+ WithOrientation(0.0f), WithDistance(0.0f)))));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
+}
+
+/**
+ * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
+ * pointer acceleration or speed processing should not be applied.
+ */
+TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
+ /*highThreshold=*/100.f, /*acceleration=*/10.f);
+ mReaderConfiguration.pointerVelocityControlParameters = testParams;
+ mFakePolicy->setVelocityControlParams(testParams);
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Move and verify scale is applied.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
+ NotifyMotionArgs motionArgs = std::get<NotifyMotionArgs>(args.front());
+ const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_GT(relX, 10);
+ ASSERT_GT(relY, 20);
+ args.clear();
+
+ // Enable Pointer Capture
+ setPointerCapture(true);
+
+ // Move and verify scale is not applied.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(10, 20)))));
+}
+
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+class CursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ CursorInputMapperUnitTestBase::SetUp();
+ }
+};
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ mFakePolicy->clearViewports();
+ mFakePointerController->clearBounds();
+ createMapper();
+
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ // Initially there should not be a valid motion range because there's no viewport or pointer
+ // bounds.
+ 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);
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(systemTime(), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ InputDeviceInfo info2;
+ mMapper->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(CursorInputMapperUnitTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ // press BTN_LEFT, release BTN_LEFT
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f)))));
+ args.clear();
+
+ // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+}
+
+class CursorInputMapperButtonKeyTestWithChoreographer
+ : public CursorInputMapperUnitTestWithChoreographer,
+ public testing::WithParamInterface<
+ std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
+ int32_t /*expectedKeyCode*/>> {};
+
+TEST_P(CursorInputMapperButtonKeyTestWithChoreographer,
+ ProcessShouldHandleButtonKeyWithZeroCoords) {
+ auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+ WithKeyCode(expectedKeyCode))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(expectedButtonState),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(expectedButtonState),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
+ WithKeyCode(expectedKeyCode)))));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ SideExtraBackAndForward, CursorInputMapperButtonKeyTestWithChoreographer,
+ testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD),
+ std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD,
+ AKEYCODE_FORWARD)));
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f),
+ WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
+ WithOrientation(0.0f), WithDistance(0.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
+ /*highThreshold=*/100.f, /*acceleration=*/10.f);
+ mReaderConfiguration.pointerVelocityControlParameters = testParams;
+ mFakePolicy->setVelocityControlParams(testParams);
+ createMapper();
+
+ NotifyMotionArgs motionArgs;
+ std::list<NotifyArgs> args;
+
+ // Move and verify scale is applied.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
+ motionArgs = std::get<NotifyMotionArgs>(args.front());
+ const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_GT(relX, 10);
+ ASSERT_GT(relY, 20);
+ args.clear();
+
+ // Enable Pointer Capture
+ setPointerCapture(true);
+
+ // Move and verify scale is not applied.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))));
+ motionArgs = std::get<NotifyMotionArgs>(args.front());
+ const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_EQ(10, relX2);
+ ASSERT_EQ(20, relY2);
+}
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
+ // Set up the default display.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_90,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+
+ // Set up the secondary display as the display on which the pointer should be shown.
+ // The InputDevice is not associated with any display.
+ mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ ui::ROTATION_0, /*isActive=*/true, "local:1", NO_PORT,
+ ViewportType::EXTERNAL);
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ // Ensure input events are generated without display ID or coords, because they will be decided
+ // later by PointerChoreographer.
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCoords(0.0f, 0.0f)))));
+}
+
+namespace {
+
+// Minimum timestamp separation between subsequent input events from a Bluetooth device.
+constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+// Maximum smoothing time delta so that we don't generate events too far into the future.
+constexpr nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+
+} // namespace
+
+class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ SetUpWithBus(BUS_BLUETOOTH);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+};
+
+TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // 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;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // 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;
+
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+
+ // 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++) {
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(cappedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningNotUsed) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // 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;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+}
+
+// --- BluetoothCursorInputMapperUnitTestWithChoreographer ---
+
+class BluetoothCursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ SetUpWithBus(BUS_BLUETOOTH);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+};
+
+TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmoothening) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // 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;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningIsCapped) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // 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;
+
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+
+ // 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++) {
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(cappedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningNotUsed) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // 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;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
}
} // namespace android
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..fb2db06
--- /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, uint32_t, 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..31e1173 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -28,6 +28,10 @@
mMaxY = maxY;
}
+void FakePointerController::clearBounds() {
+ mHaveBounds = false;
+}
+
const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() {
return mSpotsByDisplay;
}
@@ -42,11 +46,35 @@
}
int32_t FakePointerController::getDisplayId() const {
- return mDisplayId;
+ if (!mDisplayId) {
+ return ADISPLAY_ID_NONE;
+ }
+ return *mDisplayId;
}
void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
mDisplayId = viewport.displayId;
+ setBounds(viewport.logicalLeft, viewport.logicalTop, viewport.logicalRight - 1,
+ viewport.logicalBottom - 1);
+}
+
+void FakePointerController::updatePointerIcon(PointerIconStyle iconId) {
+ ASSERT_FALSE(mIconStyle.has_value()) << "Pointer icon was set more than once";
+ mIconStyle = iconId;
+}
+
+void FakePointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+ ASSERT_FALSE(mCustomIconStyle.has_value()) << "Custom pointer icon was set more than once";
+ mCustomIconStyle = icon.style;
+}
+
+void FakePointerController::assertViewportSet(int32_t displayId) {
+ ASSERT_TRUE(mDisplayId);
+ ASSERT_EQ(displayId, mDisplayId);
+}
+
+void FakePointerController::assertViewportNotSet() {
+ ASSERT_EQ(std::nullopt, mDisplayId);
}
void FakePointerController::assertPosition(float x, float y) {
@@ -55,6 +83,32 @@
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());
+}
+
+void FakePointerController::assertPointerIconSet(PointerIconStyle iconId) {
+ ASSERT_TRUE(mIconStyle) << "Pointer icon style was not set";
+ ASSERT_EQ(iconId, mIconStyle);
+ mIconStyle.reset();
+}
+
+void FakePointerController::assertPointerIconNotSet() {
+ ASSERT_EQ(std::nullopt, mIconStyle);
+}
+
+void FakePointerController::assertCustomPointerIconSet(PointerIconStyle iconId) {
+ ASSERT_TRUE(mCustomIconStyle) << "Custom pointer icon was not set";
+ ASSERT_EQ(iconId, mCustomIconStyle);
+ mCustomIconStyle.reset();
+}
+
+void FakePointerController::assertCustomPointerIconNotSet() {
+ ASSERT_EQ(std::nullopt, mCustomIconStyle);
+}
+
bool FakePointerController::isPointerShown() {
return mIsPointerShown;
}
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index c374267..061ae62 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -24,25 +24,40 @@
namespace android {
+struct SpriteIcon {
+ PointerIconStyle style;
+};
+
class FakePointerController : public PointerControllerInterface {
public:
virtual ~FakePointerController() {}
void setBounds(float minX, float minY, float maxX, float maxY);
+ void clearBounds();
const std::map<int32_t, std::vector<int32_t>>& getSpots();
void setPosition(float x, float y) override;
FloatPoint getPosition() const override;
int32_t getDisplayId() const override;
void setDisplayViewport(const DisplayViewport& viewport) override;
+ void updatePointerIcon(PointerIconStyle iconId) override;
+ void setCustomPointerIcon(const SpriteIcon& icon) override;
+ void fade(Transition) override;
+ void assertViewportSet(int32_t displayId);
+ void assertViewportNotSet();
void assertPosition(float x, float y);
+ void assertSpotCount(int32_t displayId, int32_t count);
+ void assertPointerIconSet(PointerIconStyle iconId);
+ void assertPointerIconNotSet();
+ void assertCustomPointerIconSet(PointerIconStyle iconId);
+ void assertCustomPointerIconNotSet();
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;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
@@ -52,8 +67,10 @@
bool mHaveBounds{false};
float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
float mX{0}, mY{0};
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ std::optional<int32_t> mDisplayId;
bool mIsPointerShown{false};
+ std::optional<PointerIconStyle> mIconStyle;
+ std::optional<PointerIconStyle> mCustomIconStyle;
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..1630769 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,9 +35,22 @@
namespace android {
-using testing::AllOf;
+namespace input_flags = com::android::input::flags;
-class GestureConverterTest : public testing::Test {
+namespace {
+
+const auto TOUCHPAD_PALM_REJECTION =
+ ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection);
+const auto TOUCHPAD_PALM_REJECTION_V2 =
+ ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection);
+
+} // namespace
+
+using testing::AllOf;
+using testing::ElementsAre;
+using testing::VariantWith;
+
+class GestureConverterTestBase : public testing::Test {
protected:
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr int32_t EVENTHUB_ID = 1;
@@ -83,22 +97,50 @@
std::shared_ptr<FakePointerController> mFakePointerController;
};
+class GestureConverterTest : public GestureConverterTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ GestureConverterTestBase::SetUp();
+ }
+};
+
TEST_F(GestureConverterTest, Move) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
- ASSERT_EQ(1u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+
+ // The same gesture again should only repeat the HOVER_MOVE and cursor position change, not the
+ // HOVER_ENTER.
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 10, POINTER_Y + 20),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 10, POINTER_Y + 20));
}
TEST_F(GestureConverterTest, Move_Rotated) {
@@ -108,14 +150,20 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
- ASSERT_EQ(1u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X + 10, POINTER_Y + 5),
+ WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5));
}
@@ -129,67 +177,87 @@
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
/* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
- ASSERT_EQ(3u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
- AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
- AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
// Then release the left button
Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
/* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture);
- ASSERT_EQ(1u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
// Finally release the right button
Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
/* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture);
- ASSERT_EQ(3u, args.size());
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+TEST_F(GestureConverterTest, ButtonDownAfterMoveExitsHover) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE,
+ /*is_tap=*/false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+ ASSERT_THAT(args.front(),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))));
}
TEST_F(GestureConverterTest, DragWithButton) {
@@ -201,32 +269,33 @@
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE,
/* is_tap= */ false);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
- ASSERT_EQ(2u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
// Move
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
- ASSERT_EQ(1u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
@@ -234,24 +303,27 @@
Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
/* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture);
- ASSERT_EQ(3u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll) {
@@ -261,50 +333,59 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
- ASSERT_EQ(2u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDownTime(downTime),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X, POINTER_Y - 10),
- WithGestureScrollDistance(0, 10, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X, POINTER_Y - 10),
+ WithGestureScrollDistance(0, 10, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X, POINTER_Y - 15),
- WithGestureScrollDistance(0, 5, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X, POINTER_Y - 15),
+ WithGestureScrollDistance(0, 5, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(POINTER_X, POINTER_Y - 15),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X, POINTER_Y - 15),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll_Rotated) {
@@ -315,43 +396,54 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
- ASSERT_EQ(2u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDownTime(downTime),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X - 10, POINTER_Y),
- WithGestureScrollDistance(0, 10, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X - 10, POINTER_Y),
+ WithGestureScrollDistance(0, 10, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X - 15, POINTER_Y),
- WithGestureScrollDistance(0, 5, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
-
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X - 15, POINTER_Y),
+ WithGestureScrollDistance(0, 5, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(POINTER_X - 15, POINTER_Y),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X - 15, POINTER_Y),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
@@ -360,21 +452,22 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
- ASSERT_EQ(1u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionClassification(MotionClassification::NONE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
@@ -383,20 +476,21 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
// Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
// need to use another gesture type, like pinch.
Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture);
ASSERT_FALSE(args.empty());
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON));
}
@@ -408,17 +502,19 @@
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/0);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5,
/*dy=*/10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
ASSERT_EQ(1u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionClassification(MotionClassification::NONE));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionClassification(MotionClassification::NONE))));
}
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
@@ -428,16 +524,17 @@
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
/*dy=*/5);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
// Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
// need to use another gesture type, like pinch.
Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture);
ASSERT_FALSE(args.empty());
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0)));
@@ -454,7 +551,8 @@
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
ASSERT_EQ(4u, args.size());
// Three fake fingers should be created. We don't actually care where they are, so long as they
@@ -505,7 +603,7 @@
Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 0, /* dy= */ 5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
ASSERT_EQ(1u, args.size());
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
@@ -522,30 +620,42 @@
EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15);
Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
- ASSERT_EQ(3u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(3),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
@@ -556,7 +666,8 @@
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
ASSERT_EQ(4u, args.size());
// Three fake fingers should be created. We don't actually care where they are, so long as they
@@ -598,7 +709,7 @@
Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 0, /* dy= */ 5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
ASSERT_EQ(1u, args.size());
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
@@ -613,23 +724,27 @@
EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
- ASSERT_EQ(3u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
@@ -639,7 +754,8 @@
Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 10, /* dy= */ 0);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
ASSERT_EQ(5u, args.size());
// Four fake fingers should be created. We don't actually care where they are, so long as they
@@ -702,7 +818,7 @@
Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 5, /* dy= */ 0);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
ASSERT_EQ(1u, args.size());
arg = std::get<NotifyMotionArgs>(args.front());
ASSERT_THAT(arg,
@@ -721,38 +837,52 @@
EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture);
- ASSERT_EQ(4u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(4),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
@@ -762,51 +892,63 @@
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
- ASSERT_EQ(2u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithCoords(POINTER_X - 100, POINTER_Y),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCoords(1, POINTER_X + 100, POINTER_Y),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(0.8f, EPSILON),
- WithPointerCoords(0, POINTER_X - 80, POINTER_Y),
- WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(0.8f, EPSILON),
+ WithPointerCoords(0, POINTER_X - 80, POINTER_Y),
+ WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
- ASSERT_EQ(2u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
@@ -816,51 +958,63 @@
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
- ASSERT_EQ(2u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithCoords(POINTER_X - 100, POINTER_Y), WithPointerCount(1u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCoords(1, POINTER_X + 100, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithCoords(POINTER_X - 100, POINTER_Y),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCoords(1, POINTER_X + 100, POINTER_Y),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 1.2, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.2f, EPSILON),
- WithPointerCoords(0, POINTER_X - 120, POINTER_Y),
- WithPointerCoords(1, POINTER_X + 120, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.2f, EPSILON),
+ WithPointerCoords(0, POINTER_X - 120, POINTER_Y),
+ WithPointerCoords(1, POINTER_X + 120, POINTER_Y),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
- ASSERT_EQ(2u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
@@ -870,21 +1024,22 @@
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*dz=*/1.2, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
- ASSERT_EQ(1u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- WithMotionClassification(MotionClassification::NONE));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionClassification(MotionClassification::NONE))));
}
TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
@@ -894,21 +1049,22 @@
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*dz=*/1.2, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
// Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
// need to use another gesture type, like scroll.
Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1,
/*dy=*/0);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
ASSERT_FALSE(args.empty());
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON));
}
@@ -921,28 +1077,33 @@
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
/*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_EQ(3u, args.size());
-
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
@@ -951,18 +1112,25 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(POINTER_X, POINTER_Y - 10),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X, POINTER_Y - 10),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
@@ -972,31 +1140,40 @@
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/10);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_EQ(3u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ResetDuringPinch) {
@@ -1006,22 +1183,30 @@
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_EQ(2u, args.size());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, FlingTapDown) {
@@ -1031,10 +1216,11 @@
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
WithDisplayId(ADISPLAY_ID_DEFAULT)));
@@ -1051,52 +1237,57 @@
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
-
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT,
/* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture);
- ASSERT_EQ(5u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Click) {
@@ -1107,61 +1298,71 @@
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
-
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT,
/* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture);
- ASSERT_EQ(2u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
/* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
-
- ASSERT_EQ(3u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
-TEST_F(GestureConverterTest, TapWithTapToClickDisabled) {
+TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
+ REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
+ nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
+
// Tap should be ignored when disabled
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1169,22 +1370,16 @@
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
-
- Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime,
/* down= */ GESTURES_BUTTON_LEFT,
/* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
// no events should be generated
ASSERT_EQ(0u, args.size());
@@ -1193,7 +1388,97 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
+TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
+ nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
+
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+
+ // taps before the threshold should still be ignored
+ currentTime += TAP_ENABLE_DELAY_NANOS.count();
+ flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0)));
+
+ tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // taps after the threshold should be recognised
+ currentTime += 1;
+ flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0)));
+
+ tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ ASSERT_EQ(6u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithRelativeMotion(0.f, 0.f), WithButtonState(0)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(1),
+ WithRelativeMotion(0.f, 0.f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithRelativeMotion(0.f, 0.f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithRelativeMotion(0.f, 0.f),
+ WithButtonState(0)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithRelativeMotion(0, 0),
+ WithButtonState(0)));
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
// Click should still produce button press/release events
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1203,64 +1488,71 @@
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture);
-
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_LEFT,
/* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture);
- ASSERT_EQ(2u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
/* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
- ASSERT_EQ(3u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
- args.pop_front();
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
// Future taps should be re-enabled
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);
@@ -1269,19 +1561,1533 @@
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
- ASSERT_EQ(1u, args.size());
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
-
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ // We don't need to check args here, since it's covered by the Move test.
// Future taps should be re-enabled
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
+TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
+ const nsecs_t gestureStartTime = 1000;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ // Start a move gesture at gestureStartTime
+ Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
+
+ // Key presses with IME connection should cancel ongoing move gesture
+ nsecs_t currentTime = gestureStartTime + 100;
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ mReader->getContext()->setLastKeyDownTimestamp(currentTime);
+ moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
+ args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT))));
+
+ // any updates in existing move gesture should be ignored
+ moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
+ args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture);
+ ASSERT_EQ(0u, args.size());
+
+ // New gesture should not be affected
+ currentTime += 100;
+ moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
+ args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
+}
+
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+class GestureConverterTestWithChoreographer : public GestureConverterTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ GestureConverterTestBase::SetUp();
+ }
+};
+
+TEST_F(GestureConverterTestWithChoreographer, Move) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(0, 0), WithRelativeMotion(-5, 10),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ // The same gesture again should only repeat the HOVER_MOVE, not the HOVER_ENTER.
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(0, 0), WithRelativeMotion(10, 5),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ // Press left and right buttons at once
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ // Then release the left button
+ Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ // Finally release the right button
+ Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ButtonDownAfterMoveExitsHover) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE,
+ /*is_tap=*/false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+ ASSERT_THAT(args.front(),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, DragWithButton) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ // Press the button
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE,
+ /* is_tap= */ false);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ // Move
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ // Release the button
+ Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll) {
+ const nsecs_t downTime = 12345;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0, 0),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(0, -10),
+ WithGestureScrollDistance(0, 10, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15),
+ WithGestureScrollDistance(0, 5, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0, -15),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) {
+ const nsecs_t downTime = 12345;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0, 0),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER), WithDownTime(downTime),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(-10, 0),
+ WithGestureScrollDistance(0, 10, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0),
+ WithGestureScrollDistance(0, 5, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(-15, 0),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionClassification(MotionClassification::NONE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+
+ // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+ // need to use another gesture type, like pinch.
+ Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture);
+ ASSERT_FALSE(args.empty());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/0);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5,
+ /*dy=*/10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionClassification(MotionClassification::NONE))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
+ /*dy=*/5);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+
+ // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+ // need to use another gesture type, like pinch.
+ Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture);
+ ASSERT_FALSE(args.empty());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) {
+ // The gestures library will "lock" a swipe into the dimension it starts in. For example, if you
+ // start swiping up and then start moving left or right, it'll return gesture events with only Y
+ // deltas until you lift your fingers and start swiping again. That's why each of these tests
+ // only checks movement in one dimension.
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
+ /* dy= */ 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_EQ(4u, args.size());
+
+ // Three fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 10);
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 10);
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 10);
+
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 0, /* dy= */ 5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 15);
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 15);
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15);
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(3),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setOrientation(ui::ROTATION_90);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
+ /* dy= */ 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_EQ(4u, args.size());
+
+ // Three fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+
+ Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 0, /* dy= */ 5);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 10, /* dy= */ 0);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_EQ(5u, args.size());
+
+ // Four fake fingers should be created. We don't actually care where they are, so long as they
+ // move appropriately.
+ NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger0Start = arg.pointerCoords[0];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger1Start = arg.pointerCoords[1];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger2Start = arg.pointerCoords[2];
+ args.pop_front();
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ PointerCoords finger3Start = arg.pointerCoords[3];
+ args.pop_front();
+
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 10);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+ EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
+
+ Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dx= */ 5, /* dy= */ 0);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+ ASSERT_EQ(1u, args.size());
+ arg = std::get<NotifyMotionArgs>(args.front());
+ ASSERT_THAT(arg,
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 15);
+ EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
+ EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
+ EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
+ EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
+
+ Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(4u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithGestureSwipeFingerCount(4),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithCoords(-100, 0), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCoords(1, 100, 0), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(0.8f, EPSILON),
+ WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithCoords(-100, 0), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCoords(1, 100, 0), WithPointerCount(2u),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* dz= */ 1.1, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.1f, EPSILON),
+ WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionClassification(MotionClassification::NONE))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGesture) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
+
+ Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_END);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
+
+ // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
+ // need to use another gesture type, like scroll.
+ Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1,
+ /*dy=*/0);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
+ ASSERT_FALSE(args.empty());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(0), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0, -10),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(ToolType::FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(
+ MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON),
+ WithPointerCount(1u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0),
+ WithMotionClassification(MotionClassification::NONE),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Tap) {
+ // Tap should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F(GestureConverterTestWithChoreographer, Click) {
+ // Click should produce button press/release events
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
+ REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
+ nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
+
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabledWithDelay,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
+ nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
+
+ // Tap should be ignored when disabled
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+
+ // taps before the threshold should still be ignored
+ currentTime += TAP_ENABLE_DELAY_NANOS.count();
+ flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0)));
+
+ tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ // no events should be generated
+ ASSERT_EQ(0u, args.size());
+
+ // taps after the threshold should be recognised
+ currentTime += 1;
+ flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
+
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0)));
+
+ tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
+ args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
+
+ ASSERT_EQ(6u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithRelativeMotion(0.f, 0.f), WithButtonState(0)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(1),
+ WithRelativeMotion(0.f, 0.f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithRelativeMotion(0.f, 0.f)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithRelativeMotion(0.f, 0.f),
+ WithButtonState(0)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithRelativeMotion(0, 0),
+ WithButtonState(0)));
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+ // Click should still produce button press/release events
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
+ /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+ // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+ Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPressure(1.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE,
+ /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithCoords(0, 0), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+ // initially disable tap-to-click
+ mReader->getContext()->setPreventingTouchpadTaps(true);
+
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+ // We don't need to check args here, since it's covered by the Move test.
+
+ // Future taps should be re-enabled
+ ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
+}
+
+TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMove,
+ REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
+ const nsecs_t gestureStartTime = 1000;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+
+ // Start a move gesture at gestureStartTime
+ Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
+
+ // Key presses with IME connection should cancel ongoing move gesture
+ nsecs_t currentTime = gestureStartTime + 100;
+ mFakePolicy->setIsInputMethodConnectionActive(true);
+ mReader->getContext()->setLastKeyDownTimestamp(currentTime);
+ moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
+ args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT))));
+
+ // any updates in existing move gesture should be ignored
+ moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
+ args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture);
+ ASSERT_EQ(0u, args.size());
+
+ // New gesture should not be affected
+ currentTime += 100;
+ moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
+ args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 5bea2ba..ff9bd9e 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -18,6 +18,8 @@
#include <memory>
#include <EventHub.h>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gtest/gtest.h>
#include <linux/input-event-codes.h>
#include <utils/StrongPointer.h>
@@ -31,6 +33,13 @@
namespace android {
+namespace {
+
+const auto REPORT_PALMS =
+ ACONFIG_FLAG(com::android::input::flags, report_palms_to_gestures_library);
+
+} // namespace
+
class HardwareStateConverterTest : public testing::Test {
public:
HardwareStateConverterTest()
@@ -192,7 +201,8 @@
EXPECT_EQ(0u, finger2.flags);
}
-TEST_F(HardwareStateConverterTest, OnePalm) {
+TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmDisableReportPalms,
+ REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
@@ -207,7 +217,25 @@
EXPECT_EQ(0, schs->state.finger_cnt);
}
-TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) {
+TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms,
+ REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
+ EXPECT_EQ(1, schs->state.finger_cnt);
+ EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type);
+}
+
+TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmDisableReportPalms,
+ REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
@@ -252,6 +280,56 @@
EXPECT_NEAR(95, newFinger.position_y, EPSILON);
}
+TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms,
+ REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
+ processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
+ EXPECT_EQ(1, schs->state.finger_cnt);
+ EXPECT_EQ(FingerState::ToolType::kFinger, schs->state.fingers[0].tool_type);
+
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ schs = processSync(ARBITRARY_TIME);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
+ ASSERT_EQ(1, schs->state.finger_cnt);
+ EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type);
+
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97);
+
+ schs = processSync(ARBITRARY_TIME);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
+ EXPECT_EQ(1, schs->state.finger_cnt);
+ EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type);
+
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55);
+ processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95);
+ schs = processSync(ARBITRARY_TIME);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.touch_cnt);
+ ASSERT_EQ(1, schs->state.finger_cnt);
+ const FingerState& newFinger = schs->state.fingers[0];
+ EXPECT_EQ(FingerState::ToolType::kFinger, newFinger.tool_type);
+ EXPECT_EQ(123, newFinger.tracking_id);
+ EXPECT_NEAR(55, newFinger.position_x, EPSILON);
+ EXPECT_NEAR(95, newFinger.position_y, EPSILON);
+}
+
TEST_F(HardwareStateConverterTest, ButtonPressed) {
processAxis(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3c87f71..c92736e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,9 +16,11 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
+#include "FakeApplicationHandle.h"
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/silent_death_test.h>
#include <android-base/stringprintf.h>
@@ -52,6 +54,7 @@
using namespace ftl::flag_operators;
using testing::AllOf;
+using testing::Not;
namespace {
@@ -112,8 +115,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 +338,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 +349,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 +369,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 +418,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 +468,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();
@@ -517,7 +549,7 @@
}
}
- void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {}
nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
@@ -526,9 +558,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,
@@ -537,7 +572,8 @@
/** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
* essentially a passthrough for notifySwitch.
*/
- mLastNotifySwitch = NotifySwitchArgs(/*id=*/1, when, policyFlags, switchValues, switchMask);
+ mLastNotifySwitch =
+ NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask);
}
void pokeUserActivity(nsecs_t, int32_t, int32_t) override {
@@ -545,6 +581,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 +626,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());
@@ -796,7 +837,8 @@
}
TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
- NotifySwitchArgs args(/*id=*/10, /*eventTime=*/20, /*policyFlags=*/0, /*switchValues=*/1,
+ NotifySwitchArgs args(InputEvent::nextId(), /*eventTime=*/20, /*policyFlags=*/0,
+ /*switchValues=*/1,
/*switchMask=*/2);
mDispatcher->notifySwitch(args);
@@ -814,37 +856,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 +883,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 +913,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);
}
@@ -1031,12 +1054,10 @@
}
if (event->getType() == InputEventType::KEY) {
KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
- ADD_FAILURE() << "Received key event "
- << KeyEvent::actionToString(keyEvent.getAction());
+ ADD_FAILURE() << "Received key event " << keyEvent;
} else if (event->getType() == InputEventType::MOTION) {
MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
- ADD_FAILURE() << "Received motion event "
- << MotionEvent::actionToString(motionEvent.getAction());
+ ADD_FAILURE() << "Received motion event " << motionEvent;
} else if (event->getType() == InputEventType::FOCUS) {
FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
ADD_FAILURE() << "Received focus event, hasFocus = "
@@ -1054,12 +1075,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;
@@ -1163,6 +1184,11 @@
mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
}
+ void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH,
+ shouldGlobalStylusBlockTouch);
+ }
+
void setAlpha(float alpha) { mInfo.alpha = alpha; }
void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
@@ -1202,31 +1228,27 @@
void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
- KeyEvent* consumeKey() {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- if (event == nullptr) {
- ADD_FAILURE() << "Consume failed : no event";
- return nullptr;
+ const KeyEvent& consumeKey(bool handled = true) {
+ const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
+ if (event.getType() != InputEventType::KEY) {
+ LOG(FATAL) << "Instead of key event, got " << event;
}
- if (event->getType() != InputEventType::KEY) {
- ADD_FAILURE() << "Instead of key event, got " << *event;
- return nullptr;
- }
- return static_cast<KeyEvent*>(event);
+ return static_cast<const KeyEvent&>(event);
}
void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
- KeyEvent* keyEvent = consumeKey();
- ASSERT_NE(nullptr, keyEvent) << "Did not get a key event, but expected " << matcher;
- ASSERT_THAT(*keyEvent, matcher);
+ const KeyEvent& keyEvent = consumeKey();
+ ASSERT_THAT(keyEvent, matcher);
}
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags);
+ consumeKeyEvent(AllOf(WithKeyAction(ACTION_DOWN), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+ consumeKeyEvent(AllOf(WithKeyAction(ACTION_UP), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
@@ -1248,44 +1270,46 @@
void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
std::optional<int32_t> expectedFlags = std::nullopt) {
- consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
+ consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ testing::Conditional(expectedDisplayId.has_value(),
+ WithDisplayId(*expectedDisplayId), testing::_),
+ testing::Conditional(expectedFlags.has_value(), WithFlags(*expectedFlags),
+ testing::_)));
}
void consumeMotionPointerDown(int32_t pointerIdx,
int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeEvent(InputEventType::MOTION, action, expectedDisplayId, expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
- expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
}
void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
- consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId,
- expectedFlags);
+ consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE),
+ WithDisplayId(expectedDisplayId), WithFlags(expectedFlags)));
}
- void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- MotionEvent* motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent);
- EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent->getActionMasked());
- EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getX());
- EXPECT_EQ(0.f, motionEvent->getRawPointerCoords(0)->getY());
+ void consumeMotionOutsideWithZeroedCoords() {
+ consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), WithRawCoords(0, 0)));
}
void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
@@ -1300,18 +1324,15 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
- void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
- MotionEvent* motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
- ASSERT_THAT(*motionEvent, matcher);
- }
-
- void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
- std::optional<int32_t> expectedDisplayId,
- std::optional<int32_t> expectedFlags) {
- ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
- mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
- expectedFlags);
+ const MotionEvent& consumeMotionEvent(
+ const ::testing::Matcher<MotionEvent>& matcher = testing::_) {
+ const InputEvent& event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event.getType() != InputEventType::MOTION) {
+ LOG(FATAL) << "Instead of motion event, got " << event;
+ }
+ const auto& motionEvent = static_cast<const MotionEvent&>(event);
+ EXPECT_THAT(motionEvent, matcher);
+ return motionEvent;
}
void consumeDragEvent(bool isExiting, float x, float y) {
@@ -1342,26 +1363,6 @@
mInputReceiver->sendTimeline(inputEventId, timeline);
}
- InputEvent* consume(std::chrono::milliseconds timeout) {
- if (mInputReceiver == nullptr) {
- return nullptr;
- }
- return mInputReceiver->consume(timeout);
- }
-
- MotionEvent* consumeMotion() {
- InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- if (event == nullptr) {
- ADD_FAILURE() << "Consume failed : no event";
- return nullptr;
- }
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << "Instead of motion event, got " << *event;
- return nullptr;
- }
- return static_cast<MotionEvent*>(event);
- }
-
void assertNoEvents() {
if (mInputReceiver == nullptr &&
mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
@@ -1393,48 +1394,56 @@
std::shared_ptr<FakeInputReceiver> mInputReceiver;
static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
friend class sp<FakeWindowHandle>;
+
+ const InputEvent& consume(std::chrono::milliseconds timeout, bool handled = true) {
+ if (mInputReceiver == nullptr) {
+ LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
+ }
+ InputEvent* event = mInputReceiver->consume(timeout, handled);
+ if (event == nullptr) {
+ LOG(FATAL) << "Consume failed: no event";
+ }
+ return *event;
+ }
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
class FakeMonitorReceiver {
public:
- FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId) {
- base::Result<std::unique_ptr<InputChannel>> channel =
- dispatcher.createInputMonitor(displayId, name, MONITOR_PID);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
- }
+ FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId)
+ : mInputReceiver(*dispatcher.createInputMonitor(displayId, name, MONITOR_PID), name) {}
- sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+ sp<IBinder> getToken() { return mInputReceiver.getToken(); }
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
}
std::optional<int32_t> receiveEvent() {
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ return mInputReceiver.receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
}
- void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver.finishEvent(consumeSeq); }
void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
- expectedDisplayId, expectedFlags);
+ mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
}
void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeMotionEvent(
+ mInputReceiver.consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
WithDisplayId(expectedDisplayId),
WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
@@ -1443,20 +1452,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(
@@ -1560,9 +1569,9 @@
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, POLICY_FLAG_PASS_TO_USER, action, /*flags=*/0, AKEYCODE_A, KEY_A,
- AMETA_NONE, currentTime);
+ NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
+ AINPUT_SOURCE_KEYBOARD, displayId, POLICY_FLAG_PASS_TO_USER, action,
+ /*flags=*/0, AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
return args;
}
@@ -1571,9 +1580,9 @@
int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, AMETA_META_ON,
- currentTime);
+ NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
+ AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C,
+ AMETA_META_ON, currentTime);
return args;
}
@@ -1582,9 +1591,9 @@
int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
- displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, KEY_ASSISTANT,
- AMETA_NONE, currentTime);
+ NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
+ AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST,
+ KEY_ASSISTANT, AMETA_NONE, currentTime);
return args;
}
@@ -1612,9 +1621,9 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
- NotifyMotionArgs args(/*id=*/0, currentTime, /*readTime=*/0, DEVICE_ID, source, displayId,
- POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0, /*flags=*/0,
- AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
+ NotifyMotionArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, source,
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0,
+ /*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
@@ -1633,7 +1642,8 @@
static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(
const PointerCaptureRequest& request) {
- return NotifyPointerCaptureChangedArgs(/*id=*/0, systemTime(SYSTEM_TIME_MONOTONIC), request);
+ return NotifyPointerCaptureChangedArgs(InputEvent::nextId(), systemTime(SYSTEM_TIME_MONOTONIC),
+ request);
}
} // namespace
@@ -1763,8 +1773,10 @@
mDispatcher->onWindowInfosChanged(
{{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(200))
+ .build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both foreground window and its wallpaper should receive the touch down
@@ -1772,11 +1784,13 @@
wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 200}))
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(200))
+ .build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- foregroundWindow->consumeMotionMove();
+ foregroundWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
// Now the foreground window goes away, but the wallpaper stays
@@ -2160,6 +2174,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 +2591,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 +2622,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 +2676,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 +3007,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 +3051,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 +3284,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 +3310,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 +3320,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 +3332,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,12 +3340,156 @@
.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();
}
/**
+ * If stylus is down anywhere on the screen, then touches should not be delivered to windows that
+ * have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH.
+ *
+ * Two windows: one on the left and one on the right.
+ * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config.
+ * Stylus down on the left window, and then touch down on the right window.
+ * Check that the right window doesn't get touches while the stylus is down on the left window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusDownBlocksTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> sbtRightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT);
+ sbtRightWindow->setFrame(Rect(100, 100, 200, 200));
+ sbtRightWindow->setGlobalStylusBlocksTouch(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 5;
+ const int32_t touchDeviceId = 4;
+
+ // Stylus down in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Finger tap on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+
+ // The touch should be blocked, because stylus is down somewhere else on screen!
+ sbtRightWindow->assertNoEvents();
+
+ // Continue stylus motion, and ensure it's not impacted.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithDeviceId(stylusDeviceId)));
+
+ // Now that the stylus gesture is done, touches should be getting delivered correctly.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153))
+ .deviceId(touchDeviceId)
+ .build());
+ sbtRightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
+ * If stylus is hovering anywhere on the screen, then touches should not be delivered to windows
+ * that have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH.
+ *
+ * Two windows: one on the left and one on the right.
+ * The window on the right has GLOBAL_STYLUS_BLOCKS_TOUCH config.
+ * Stylus hover on the left window, and then touch down on the right window.
+ * Check that the right window doesn't get touches while the stylus is hovering on the left window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, GlobalStylusHoverBlocksTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> sbtRightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT);
+ sbtRightWindow->setFrame(Rect(100, 100, 200, 200));
+ sbtRightWindow->setGlobalStylusBlocksTouch(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *sbtRightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 5;
+ const int32_t touchDeviceId = 4;
+
+ // Stylus hover in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(52))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Finger tap on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(151))
+ .deviceId(touchDeviceId)
+ .build());
+
+ // The touch should be blocked, because stylus is hovering somewhere else on screen!
+ sbtRightWindow->assertNoEvents();
+
+ // Continue stylus motion, and ensure it's not impacted.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(53))
+ .deviceId(stylusDeviceId)
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+
+ // Now that the stylus gesture is done, touches should be getting delivered correctly.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(153))
+ .deviceId(touchDeviceId)
+ .build());
+ sbtRightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* A spy window above a window with no input channel.
* Start hovering with a stylus device, and then tap with it.
* Ensure spy window receives the entire sequence.
@@ -3331,6 +3548,44 @@
}
/**
+ * A stale stylus HOVER_EXIT event is injected. Since it's a stale event, it should generally be
+ * rejected. But since we already have an ongoing gesture, this event should be processed.
+ * This prevents inconsistent events being handled inside the dispatcher.
+ */
+TEST_F(InputDispatcherTest, StaleStylusHoverGestureIsComplete) {
+ 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, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // Start hovering with stylus
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ NotifyMotionArgs hoverExit = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+ .build();
+ // Make this 'hoverExit' event stale
+ mFakePolicy->setStaleEventTimeout(100ms);
+ std::this_thread::sleep_for(100ms);
+
+ // It shouldn't be dropped by the dispatcher, even though it's stale.
+ mDispatcher->notifyMotion(hoverExit);
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Stylus starts hovering again! There should be no crash.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(51))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
+/**
* Start hovering with a mouse, and then tap with a touch device. Pilfer the touch stream.
* Next, click with the mouse device. Both windows (spy and regular) should receive the new mouse
* ACTION_DOWN event because that's a new gesture, and pilfering should no longer be active.
@@ -3505,20 +3760,18 @@
mDispatcher->waitForIdle();
- MotionEvent* motionEvent1 = window1->consumeMotion();
- ASSERT_NE(motionEvent1, nullptr);
+ const MotionEvent& motionEvent1 = window1->consumeMotionEvent();
window2->assertNoEvents();
- nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
- ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
+ nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
+ ASSERT_EQ(motionEvent1.getDownTime(), motionEvent1.getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- MotionEvent* motionEvent2 = window2->consumeMotion();
- ASSERT_NE(motionEvent2, nullptr);
- nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
+ const MotionEvent& motionEvent2 = window2->consumeMotionEvent();
+ nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
+ ASSERT_EQ(motionEvent2.getDownTime(), motionEvent2.getEventTime());
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
@@ -4113,8 +4366,7 @@
// When device reset happens, that key stream should be terminated with FLAG_CANCELED
// on the app side.
mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
- window->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
- AKEY_EVENT_FLAG_CANCELED);
+ window->consumeKeyUp(ADISPLAY_ID_DEFAULT, AKEY_EVENT_FLAG_CANCELED);
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
@@ -4238,6 +4490,12 @@
// Therefore, we should offset them by (100, 100) relative to the screen's top left corner.
outsideWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_OUTSIDE), WithCoords(-50, -50)));
+
+ // Ensure outsideWindow doesn't get any more events for the gesture.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{51, 51}}));
+ window->consumeMotionMove();
+ outsideWindow->assertNoEvents();
}
/**
@@ -4347,12 +4605,12 @@
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- const MotionEvent* event = window->consumeMotion();
- EXPECT_EQ(POINTER_1_DOWN, event->getAction());
- EXPECT_EQ(70, event->getX(0)); // 50 + 20
- EXPECT_EQ(90, event->getY(0)); // 50 + 40
- EXPECT_EQ(-10, event->getX(1)); // -30 + 20
- EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+ const MotionEvent& event = window->consumeMotionEvent();
+ EXPECT_EQ(POINTER_1_DOWN, event.getAction());
+ EXPECT_EQ(70, event.getX(0)); // 50 + 20
+ EXPECT_EQ(90, event.getY(0)); // 50 + 40
+ EXPECT_EQ(-10, event.getX(1)); // -30 + 20
+ EXPECT_EQ(-10, event.getY(1)); // -50 + 40
}
/**
@@ -4618,15 +4876,15 @@
EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindowDefaultDisplay->getToken()));
// windowDefaultDisplay gets cancel
- MotionEvent* event = windowDefaultDisplay->consumeMotion();
- EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event->getAction());
+ const MotionEvent& event = windowDefaultDisplay->consumeMotionEvent();
+ EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
// The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so the
// coordinates of the cancel are converted by windowDefaultDisplay's transform, the x and y
// coordinates are both 100, otherwise if the cancel event is sent to windowSecondDisplay of
// SECOND_DISPLAY_ID, the x and y coordinates are 200
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(100, event->getY(0));
+ EXPECT_EQ(100, event.getX(0));
+ EXPECT_EQ(100, event.getY(0));
}
/**
@@ -4756,19 +5014,18 @@
{PointF{150, 220}}));
firstWindow->assertNoEvents();
- const MotionEvent* event = secondWindow->consumeMotion();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+ const MotionEvent& event = secondWindow->consumeMotionEvent();
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event.getAction());
// Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event->getRawX(0));
- EXPECT_EQ(880, event->getRawY(0));
+ EXPECT_EQ(300, event.getRawX(0));
+ EXPECT_EQ(880, event.getRawY(0));
// Ensure that the x and y values are in the window's coordinate space.
// The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
// the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(80, event->getY(0));
+ EXPECT_EQ(100, event.getX(0));
+ EXPECT_EQ(80, event.getY(0));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
@@ -5842,8 +6099,7 @@
motionArgs.pointerCoords[0].getX() - 10);
mDispatcher->notifyMotion(motionArgs);
- window->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
- /*expectedFlags=*/0);
+ window->consumeMotionMove(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0);
}
/**
@@ -5913,10 +6169,9 @@
const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
mDispatcher->notifyKey(keyArgs);
- KeyEvent* event = window->consumeKey();
- ASSERT_NE(event, nullptr);
+ const KeyEvent& event = window->consumeKey();
- std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event);
ASSERT_NE(verified, nullptr);
ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY);
@@ -5957,10 +6212,9 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->notifyMotion(motionArgs);
- MotionEvent* event = window->consumeMotion();
- ASSERT_NE(event, nullptr);
+ const MotionEvent& event = window->consumeMotionEvent();
- std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(event);
ASSERT_NE(verified, nullptr);
ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION);
@@ -6464,10 +6718,265 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
}
+TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Right Window", ADISPLAY_ID_DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+ sp<FakeWindowHandle> spy =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ spy->setFrame(Rect(0, 0, 200, 100));
+ spy->setTrustedOverlay(true);
+ spy->setSpy(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ // Send hover move to the left window, and ensure hover enter is synthesized with a new eventId.
+ NotifyMotionArgs notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
+ mDispatcher->notifyMotion(notifyArgs);
+
+ const MotionEvent& leftEnter = left->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), Not(WithEventId(notifyArgs.id)),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ Not(WithEventId(notifyArgs.id)),
+ Not(WithEventId(leftEnter.getId())),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ // Send move to the right window, and ensure hover exit and enter are synthesized with new ids.
+ notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
+ {PointF{150, 50}});
+ mDispatcher->notifyMotion(notifyArgs);
+
+ const MotionEvent& leftExit = left->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), Not(WithEventId(notifyArgs.id)),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ right->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ Not(WithEventId(notifyArgs.id)),
+ Not(WithEventId(leftExit.getId())),
+ WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
+
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithEventId(notifyArgs.id)));
+}
+
+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) {
+ const KeyEvent& event = mWindow->consumeKey(handled);
+ ASSERT_THAT(event, matcher);
+ }
+};
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotNotifiedForHandledKey) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/true, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, PolicyNotifiedForUnhandledKey) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, NoFallbackRequestedByPolicy) {
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+
+ // Since the policy did not request any fallback to be generated, ensure there are no events.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, FallbackDispatchForUnhandledKey) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+
+ // Since the key was not handled, ensure the fallback event was dispatched instead.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppHandlesPreviouslyUnhandledKey) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, but handle the fallback.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ // But this time, the app handles the original key.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key is canceled.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, AppDoesNotHandleFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // App does not handle the fallback either, so ensure another fallback is not generated.
+ setFallback(AKEYCODE_C);
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, and ensure the fallback key is also released.
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, InconsistentPolicyCancelsFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, so fallback is generated.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Release the original key, but assume the policy is misbehaving and it
+ // generates an inconsistent fallback to the one from the DOWN event.
+ setFallback(AKEYCODE_C);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key reported before as DOWN is canceled due to the inconsistency.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event, so fallback is generated.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // The original key is canceled.
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+ .keyCode(AKEYCODE_A)
+ .addFlag(AKEY_EVENT_FLAG_CANCELED)
+ .build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A),
+ WithFlags(AKEY_EVENT_FLAG_CANCELED)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ // Ensure the fallback key is also canceled due to the original key being canceled.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported());
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
- static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
- static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms
+ static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
+ static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms;
std::shared_ptr<FakeApplicationHandle> mApp;
sp<FakeWindowHandle> mWindow;
@@ -6515,7 +7024,7 @@
mDispatcher->notifyKey(keyArgs);
// Window should receive key down event.
- mWindow->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+ mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT,
/*expectedFlags=*/0);
}
};
@@ -6585,10 +7094,9 @@
GTEST_SKIP() << "Flaky test (b/270393106)";
sendAndConsumeKeyDown(/*deviceId=*/1);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- KeyEvent* repeatEvent = mWindow->consumeKey();
- ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
+ const KeyEvent& repeatEvent = mWindow->consumeKey();
EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
- IdGenerator::getSource(repeatEvent->getId()));
+ IdGenerator::getSource(repeatEvent.getId()));
}
}
@@ -6598,9 +7106,8 @@
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
- KeyEvent* repeatEvent = mWindow->consumeKey();
- ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
- int32_t id = repeatEvent->getId();
+ const KeyEvent& repeatEvent = mWindow->consumeKey();
+ int32_t id = repeatEvent.getId();
EXPECT_EQ(idSet.end(), idSet.find(id));
idSet.insert(id);
}
@@ -6689,8 +7196,7 @@
mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
// Old focus should receive a cancel event.
- windowInSecondary->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
- AKEY_EVENT_FLAG_CANCELED);
+ windowInSecondary->consumeKeyUp(ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
@@ -7168,24 +7674,21 @@
void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
const std::vector<PointF>& points) {
const std::string name = window->getName();
- MotionEvent* motionEvent = window->consumeMotion();
+ const MotionEvent& motionEvent =
+ window->consumeMotionEvent(WithMotionAction(expectedAction));
- ASSERT_NE(nullptr, motionEvent)
- << name.c_str() << ": consumer should have returned non-NULL event.";
-
- ASSERT_THAT(*motionEvent, WithMotionAction(expectedAction));
- ASSERT_EQ(points.size(), motionEvent->getPointerCount());
+ ASSERT_EQ(points.size(), motionEvent.getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
float expectedY = points[i].y;
- EXPECT_EQ(expectedX, motionEvent->getX(i))
+ EXPECT_EQ(expectedX, motionEvent.getX(i))
<< "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent->getX(i);
- EXPECT_EQ(expectedY, motionEvent->getY(i))
+ << ", got " << motionEvent.getX(i);
+ EXPECT_EQ(expectedY, motionEvent.getY(i))
<< "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
- << ", got " << motionEvent->getY(i);
+ << ", got " << motionEvent.getY(i);
}
}
@@ -7404,12 +7907,15 @@
static constexpr PointF WINDOW_LOCATION = {20, 20};
void tapOnWindow() {
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
+ const auto touchingPointer = PointerBuilder(/*id=*/0, ToolType::FINGER)
+ .x(WINDOW_LOCATION.x)
+ .y(WINDOW_LOCATION.y);
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(touchingPointer)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(touchingPointer)
+ .build());
}
sp<FakeWindowHandle> addSpyWindow() {
@@ -7529,6 +8035,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();
@@ -7844,10 +8352,10 @@
std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(upSequenceNum);
// Don't finish the events yet, and send a key
- // Injection is async, so it will succeed
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
- ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT)
+ .build());
// At this point, key is still pending, and should not be sent to the application yet.
// Make sure the `assertNoEvents` check doesn't take too long. It uses
// CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood.
@@ -7856,12 +8364,12 @@
// Now tap down again. It should cause the pending key to go to the focused window right away.
tapOnWindow();
- mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd
- // the other events yet. We can finish events in any order.
+ mWindow->consumeKeyEvent(WithKeyAction(AKEY_EVENT_ACTION_DOWN)); // it doesn't matter that we
+ // haven't ack'd the other events yet. We can finish events in any order.
mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN
mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP
- mWindow->consumeMotionDown();
- mWindow->consumeMotionUp();
+ mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ mWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
mWindow->assertNoEvents();
}
@@ -7993,8 +8501,7 @@
.build()));
mFocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionUp();
- mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
- ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// We consumed all events, so no ANR
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -8070,8 +8577,7 @@
// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
tapOnFocusedWindow();
- mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
- ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// Receive the events, but don't respond
std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(downEventSequenceNum);
@@ -8203,8 +8709,7 @@
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{FOCUSED_WINDOW_LOCATION}));
- mUnfocusedWindow->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
- ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
// Touch Window 2
mDispatcher->notifyMotion(
@@ -8304,6 +8809,104 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
}
+/**
+ * If we are pruning input queue, we should never drop pointer events. Otherwise, we risk having
+ * an inconsistent event stream inside the dispatcher. In this test, we make sure that the
+ * dispatcher doesn't prune pointer events incorrectly.
+ *
+ * This test reproduces a crash in InputDispatcher.
+ * To reproduce the crash, we need to simulate the conditions for "pruning input queue" to occur.
+ *
+ * Keep the currently focused application (mApplication), and have no focused window.
+ * We set up two additional windows:
+ * 1) The navigation bar window. This simulates the system "NavigationBar", which is used in the
+ * 3-button navigation mode. This window injects a BACK button when it's touched. 2) The application
+ * window. This window is not focusable, but is touchable.
+ *
+ * We first touch the navigation bar, which causes it to inject a key. Since there's no focused
+ * window, the dispatcher doesn't process this key, and all other events inside dispatcher are now
+ * blocked. The dispatcher is waiting for 'mApplication' to add a focused window.
+ *
+ * Now, we touch "Another window". This window is owned by a different application than
+ * 'mApplication'. This causes the dispatcher to stop waiting for 'mApplication' to add a focused
+ * window. Now, the "pruning input queue" behaviour should kick in, and the dispatcher should start
+ * dropping the events from its queue. Ensure that no crash occurs.
+ *
+ * In this test, we are setting long timeouts to prevent ANRs and events dropped due to being stale.
+ * This does not affect the test running time.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PruningInputQueueShouldNotDropPointerEvents) {
+ std::shared_ptr<FakeApplicationHandle> systemUiApplication =
+ std::make_shared<FakeApplicationHandle>();
+ systemUiApplication->setDispatchingTimeout(3000ms);
+ mFakePolicy->setStaleEventTimeout(3000ms);
+ sp<FakeWindowHandle> navigationBar =
+ sp<FakeWindowHandle>::make(systemUiApplication, mDispatcher, "NavigationBar",
+ ADISPLAY_ID_DEFAULT);
+ navigationBar->setFocusable(false);
+ navigationBar->setWatchOutsideTouch(true);
+ navigationBar->setFrame(Rect(0, 0, 100, 100));
+
+ mApplication->setDispatchingTimeout(3000ms);
+ // 'mApplication' is already focused, but we call it again here to make it explicit.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
+ std::shared_ptr<FakeApplicationHandle> anotherApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> appWindow =
+ sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Another window",
+ ADISPLAY_ID_DEFAULT);
+ appWindow->setFocusable(false);
+ appWindow->setFrame(Rect(100, 100, 200, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*navigationBar->getInfo(), *appWindow->getInfo()}, {}, 0, 0});
+ // 'mFocusedWindow' is no longer in the dispatcher window list, and therefore loses focus
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Touch down the navigation bar. It consumes the touch and injects a key into the dispatcher
+ // in response.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ navigationBar->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Key will not be sent anywhere because we have no focused window. It will remain pending.
+ // Pretend we are injecting KEYCODE_BACK, but it doesn't actually matter what key it is.
+ InputEventInjectionResult result =
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ /*allowKeyRepeat=*/false);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+
+ // Finish the gesture - lift up finger and inject ACTION_UP key event
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ result = injectKey(*mDispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ /*allowKeyRepeat=*/false);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+ // The key that was injected is blocking the dispatcher, so the navigation bar shouldn't be
+ // getting any events yet.
+ navigationBar->assertNoEvents();
+
+ // Now touch "Another window". This touch is going to a different application than the one we
+ // are waiting for (which is 'mApplication').
+ // This should cause the dispatcher to drop the pending focus-dispatched events (like the key
+ // trying to be injected) and to continue processing the rest of the events in the original
+ // order.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
+ .build());
+ navigationBar->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ navigationBar->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+ appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ appWindow->assertNoEvents();
+ navigationBar->assertNoEvents();
+}
+
// These tests ensure we cannot send touch events to a window that's positioned behind a window
// that has feature NO_INPUT_CHANNEL.
// Layout:
@@ -9317,6 +9920,50 @@
mSecondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherDragTests, DragAndDropNotCancelledIfSomeOtherPointerIsPilfered) {
+ startDrag();
+
+ // No cancel event after drag start
+ mSpyWindow->assertNoEvents();
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Receives cancel for first pointer after next pointer down
+ mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ mSpyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithPointerIds({1})));
+ mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ mSpyWindow->assertNoEvents();
+
+ // Spy window calls pilfer pointers
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(mSpyWindow->getToken()));
+ mDragWindow->assertNoEvents();
+
+ const MotionEvent firstFingerMoveEvent =
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(60).y(60))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, firstFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Drag window should still receive the new event
+ mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ mDragWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
startDrag(true, AINPUT_SOURCE_STYLUS);
@@ -9499,8 +10146,7 @@
.displayId(SECOND_DISPLAY_ID)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- windowInSecondary->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- SECOND_DISPLAY_ID, /*expectedFlag=*/0);
+ windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID, /*expectedFlag=*/0);
// Update window again.
mDispatcher->onWindowInfosChanged(
{{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
@@ -9654,6 +10300,19 @@
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp());
}
+TEST_F(InputDispatcherDragTests, NoDragAndDropWithHoveringPointer) {
+ // Start hovering over the window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE,
+ ADISPLAY_ID_DEFAULT, {50, 50}));
+
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)));
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)));
+
+ ASSERT_FALSE(startDrag(/*sendDown=*/false))
+ << "Drag and drop should not work with a hovering pointer";
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
@@ -9675,6 +10334,7 @@
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
window->assertNoEvents();
// With the flag cleared, the window should get input
@@ -10552,6 +11212,25 @@
rightWindow->assertNoEvents();
}
+TEST_F(InputDispatcherPilferPointersTest, NoPilferingWithHoveringPointers) {
+ auto window = createForeground();
+ auto spy = createSpy();
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(1)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(100).y(200))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Pilfer pointers from the spy window should fail.
+ EXPECT_NE(OK, mDispatcher->pilferPointers(spy->getToken()));
+ spy->assertNoEvents();
+ window->assertNoEvents();
+}
+
class InputDispatcherStylusInterceptorTest : public InputDispatcherTest {
public:
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
@@ -10828,4 +11507,243 @@
randosWindow->assertNoEvents();
}
+using InputDispatcherPointerInWindowTest = InputDispatcherTest;
+
+TEST_F(InputDispatcherPointerInWindowTest, PointerInWindowWhenHovering) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Right Window", ADISPLAY_ID_DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+ sp<FakeWindowHandle> spy =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ spy->setFrame(Rect(0, 0, 200, 100));
+ spy->setTrustedOverlay(true);
+ spy->setSpy(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ // Hover into the left window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(50))
+ .build());
+
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Hover move to the right window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(50))
+ .build());
+
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Stop hovering.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(50))
+ .build());
+
+ right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+}
+
+TEST_F(InputDispatcherPointerInWindowTest, PointerInWindowWithSplitTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Right Window", ADISPLAY_ID_DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+ sp<FakeWindowHandle> spy =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ spy->setFrame(Rect(0, 0, 200, 100));
+ spy->setTrustedOverlay(true);
+ spy->setSpy(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ // First pointer down on left window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .build());
+
+ left->consumeMotionDown();
+ spy->consumeMotionDown();
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Second pointer down on right window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
+ .build());
+
+ left->consumeMotionMove();
+ right->consumeMotionDown();
+ spy->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/1));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/1));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/1));
+
+ // Second pointer up.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(50))
+ .build());
+
+ left->consumeMotionMove();
+ right->consumeMotionUp();
+ spy->consumeMotionEvent(WithMotionAction(POINTER_1_UP));
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/1));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/1));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/1));
+
+ // First pointer up.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .build());
+
+ left->consumeMotionUp();
+ spy->consumeMotionUp();
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+}
+
+TEST_F(InputDispatcherPointerInWindowTest, MultipleDevicesControllingOneMouse) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Right Window", ADISPLAY_ID_DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+
+ mDispatcher->onWindowInfosChanged({{*left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Hover move into the window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .rawXCursorPosition(50)
+ .rawYCursorPosition(50)
+ .deviceId(DEVICE_ID)
+ .build());
+
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Move the mouse with another device. This cancels the hovering pointer from the first device.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(51).y(50))
+ .rawXCursorPosition(51)
+ .rawYCursorPosition(50)
+ .deviceId(SECOND_DEVICE_ID)
+ .build());
+
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // TODO(b/313689709): InputDispatcher's touch state is not updated, even though the window gets
+ // a HOVER_EXIT from the first device.
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT,
+ SECOND_DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Move the mouse outside the window. Document the current behavior, where the window does not
+ // receive HOVER_EXIT even though the mouse left the window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(150).y(50))
+ .rawXCursorPosition(150)
+ .rawYCursorPosition(50)
+ .deviceId(SECOND_DEVICE_ID)
+ .build());
+
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT,
+ SECOND_DEVICE_ID,
+ /*pointerId=*/0));
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index dac4ea0..5f43bd2 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -19,29 +19,42 @@
#include <InputReaderBase.h>
#include <gtest/gtest.h>
#include <ui/Rotation.h>
+#include <utils/Timers.h>
namespace android {
using testing::Return;
-void InputMapperUnitTest::SetUp() {
+void InputMapperUnitTest::SetUpWithBus(int bus) {
mFakePointerController = std::make_shared<FakePointerController>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y);
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
EXPECT_CALL(mMockInputReaderContext, getPointerController(DEVICE_ID))
.WillRepeatedly(Return(mFakePointerController));
- EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
- InputDeviceIdentifier identifier;
- identifier.name = "device";
- identifier.location = "USB1";
- identifier.bus = 0;
+ EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get()));
- EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)).WillRepeatedly(Return(identifier));
+ EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
+
+ mIdentifier.name = "device";
+ mIdentifier.location = "USB1";
+ mIdentifier.bus = bus;
+ EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID))
+ .WillRepeatedly(Return(mIdentifier));
+ EXPECT_CALL(mMockEventHub, getConfiguration(EVENTHUB_ID)).WillRepeatedly([&](int32_t) {
+ return mPropertyMap;
+ });
+}
+
+void InputMapperUnitTest::createDevice() {
mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID,
- /*generation=*/2, identifier);
+ /*generation=*/2, mIdentifier);
+ mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
+ std::list<NotifyArgs> _ =
+ mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{});
}
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
@@ -80,9 +93,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;
@@ -206,8 +225,8 @@
return generatedArgs;
}
-void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz) {
+void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min,
+ float max, float flat, float fuzz) {
const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
@@ -218,11 +237,9 @@
ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
}
-void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y,
- float pressure, float size, float touchMajor,
- float touchMinor, float toolMajor, float toolMinor,
- float orientation, float distance,
- float scaledAxisEpsilon) {
+void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size,
+ float touchMajor, float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance, float scaledAxisEpsilon) {
ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index c2ac258..e176a65 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -32,6 +32,7 @@
#include "InterfaceMocks.h"
#include "TestConstants.h"
#include "TestInputListener.h"
+#include "input/PropertyMap.h"
namespace android {
@@ -41,7 +42,15 @@
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr float INITIAL_CURSOR_X = 400;
static constexpr float INITIAL_CURSOR_Y = 240;
- virtual void SetUp() override;
+ virtual void SetUp() override { SetUpWithBus(0); }
+ virtual void SetUpWithBus(int bus);
+
+ /**
+ * Initializes mDevice and mDeviceContext. When this happens, mDevice takes a copy of
+ * mPropertyMap, so tests that need to set configuration properties should do so before calling
+ * this. Others will most likely want to call it in their SetUp method.
+ */
+ void createDevice();
void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
@@ -52,8 +61,11 @@
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);
+ InputDeviceIdentifier mIdentifier;
MockEventHubInterface mMockEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakePointerController> mFakePointerController;
MockInputReaderContext mMockInputReaderContext;
std::unique_ptr<InputDevice> mDevice;
@@ -62,6 +74,7 @@
InputReaderConfiguration mReaderConfiguration;
// The mapper should be created by the subclasses.
std::unique_ptr<InputMapper> mMapper;
+ PropertyMap mPropertyMap;
};
/**
@@ -128,13 +141,13 @@
void resetMapper(InputMapper& mapper, nsecs_t when);
std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when);
-
- static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz);
- static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
- float size, float touchMajor, float touchMinor, float toolMajor,
- float toolMinor, float orientation, float distance,
- float scaledAxisEpsilon = 1.f);
};
+void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min,
+ float max, float flat, float fuzz);
+
+void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size,
+ float touchMajor, float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance, float scaledAxisEpsilon = 1.f);
+
} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 64ae9e8..91aa0ca 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>
@@ -96,8 +97,8 @@
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
-// Maximum smoothing time delta so that we don't generate events too far into the future.
-constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+
+namespace input_flags = com::android::input::flags;
template<typename T>
static inline T min(T a, T b) {
@@ -229,6 +230,11 @@
mResetWasCalled = false;
}
+ void assertResetWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mResetWasCalled) << "Expected reset to not have been called.";
+ }
+
void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -245,6 +251,11 @@
mProcessWasCalled = false;
}
+ void assertProcessWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mProcessWasCalled) << "Expected process to not have been called.";
+ }
+
void setKeyCodeState(int32_t keyCode, int32_t state) {
mKeyCodeStates.replaceValueFor(keyCode, state);
}
@@ -2869,6 +2880,60 @@
ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
}
+TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) {
+ mFakePolicy->clearViewports();
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+
+ mapper.assertConfigureWasCalled();
+ mapper.assertResetWasNotCalled();
+
+ RawEvent event{.when = ARBITRARY_TIME,
+ .readTime = ARBITRARY_TIME,
+ .deviceId = EVENTHUB_ID,
+ .type = EV_SYN,
+ .code = SYN_REPORT,
+ .value = 0};
+
+ // Events are processed normally.
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasCalled();
+
+ // Simulate a kernel buffer overflow, which generates a SYN_DROPPED event.
+ // This should reset the mapper.
+ event.type = EV_SYN;
+ event.code = SYN_DROPPED;
+ event.value = 0;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+ mapper.assertResetWasCalled();
+
+ // All events until the next SYN_REPORT should be dropped.
+ event.type = EV_KEY;
+ event.code = KEY_A;
+ event.value = 1;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+
+ // We get the SYN_REPORT event now, which is not forwarded to mappers.
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+
+ // The mapper receives events normally now.
+ event.type = EV_KEY;
+ event.code = KEY_B;
+ event.value = 1;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasCalled();
+}
+
// --- SwitchInputMapperTest ---
class SwitchInputMapperTest : public InputMapperTest {
@@ -4097,9 +4162,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 +4198,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,253 +4216,15 @@
float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f));
}
-TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
+// --- CursorInputMapperTest ---
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
- 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 bounds are set, then there should be a valid motion range.
- mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
-
- InputDeviceInfo info2;
- mapper.populateDeviceInfo(info2);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
- 1, 800 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE,
- 2, 480 - 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(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- InputDeviceInfo info;
- mapper.populateDeviceInfo(info);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
- -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL,
- -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TRACKBALL,
- 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs args;
-
- // Button press.
- // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Button release. Should have same down time.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(0, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(0, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyMotionArgs args;
-
- // Motion in X but not Y.
- 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(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f,
- 0.0f));
-
- // Motion in Y but not X.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyMotionArgs args;
-
- // Button press.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- // Button release.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyMotionArgs args;
-
- // Combined X, Y and Button.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f));
-
- // Move X, Y a bit while pressed.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f));
-
- // Release Button.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-}
+class CursorInputMapperTest : public CursorInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ CursorInputMapperTestBase::SetUp();
+ }
+};
TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
@@ -4470,328 +4297,6 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1));
}
-TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- 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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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], 100.0f, 200.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(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- 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],
- 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-/**
- * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
- * pointer acceleration or speed processing should not be applied.
- */
-TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) {
- addConfigurationProperty("cursor.mode", "pointer");
- const VelocityControlParameters testParams(/*scale=*/5.f, /*low threshold=*/0.f,
- /*high threshold=*/100.f, /*acceleration=*/10.f);
- mFakePolicy->setVelocityControlParams(testParams);
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- 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);
- ASSERT_EQ(10, args.pointerCoords[0].getX());
- ASSERT_EQ(20, args.pointerCoords[0].getY());
-}
-
TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
addConfigurationProperty("cursor.mode", "pointer");
CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
@@ -4906,104 +4411,68 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-// --- BluetoothCursorInputMapperTest ---
+// --- CursorInputMapperTestWithChoreographer ---
-class BluetoothCursorInputMapperTest : public CursorInputMapperTest {
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
protected:
void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
-
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
+ input_flags::enable_pointer_choreographer(true);
+ CursorInputMapperTestBase::SetUp();
}
};
-TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) {
- addConfigurationProperty("cursor.mode", "pointer");
+TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
- 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))));
+ // Set up the default display.
+ prepareDisplay(ui::ROTATION_90);
- // 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;
+ // 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);
- 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))));
- }
-}
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
-TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ // 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),
- 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))));
- }
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+ WithCoords(0.0f, 0.0f))));
}
-TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) {
- addConfigurationProperty("cursor.mode", "pointer");
+TEST_F(CursorInputMapperTestWithChoreographer,
+ ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) {
CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
- 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);
+ // 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),
- 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))));
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
+ WithCoords(0.0f, 0.0f))));
}
// --- TouchInputMapperTest ---
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..9de80af 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -48,7 +48,7 @@
class MockInputReaderContext : public InputReaderContext {
public:
MOCK_METHOD(void, updateGlobalMetaState, (), (override));
- int32_t getGlobalMetaState() override { return 0; };
+ MOCK_METHOD(int32_t, getGlobalMetaState, (), (override));
MOCK_METHOD(void, disableVirtualKeysUntil, (nsecs_t time), (override));
MOCK_METHOD(bool, shouldDropVirtualKey, (nsecs_t now, int32_t keyCode, int32_t scanCode),
@@ -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..b44529b 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;
@@ -54,6 +55,7 @@
void SetUp() override {
InputMapperUnitTest::SetUp();
+ createDevice();
// set key-codes expected in tests
for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
@@ -158,4 +160,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..5e67506
--- /dev/null
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ createDevice();
+ }
+
+ 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..193b84d 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -17,18 +17,71 @@
#include "../PointerChoreographer.h"
#include <gtest/gtest.h>
+#include <deque>
#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... {};
+struct Visitor : V... {
+ using V::operator()...;
+};
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);
+const auto TOUCHPAD_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
+
+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 +89,59 @@
TestInputListener mTestListener;
PointerChoreographer mChoreographer{mTestListener, *this};
- std::shared_ptr<PointerControllerInterface> createPointerController() { return {}; }
+ std::shared_ptr<FakePointerController> assertPointerControllerCreated(
+ ControllerType expectedType) {
+ EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created";
+ auto [type, controller] = std::move(mCreatedControllers.front());
+ EXPECT_EQ(expectedType, type);
+ mCreatedControllers.pop_front();
+ return controller;
+ }
+
+ void assertPointerControllerNotCreated() { ASSERT_TRUE(mCreatedControllers.empty()); }
+
+ 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 assertPointerControllerNotRemoved(const std::shared_ptr<FakePointerController>& pc) {
+ // See assertPointerControllerRemoved above.
+ ASSERT_GT(pc.use_count(), 1) << "Expected PointerChoreographer to hold at least one "
+ "reference to this PointerController";
+ }
+
+ void assertPointerDisplayIdNotified(int32_t displayId) {
+ ASSERT_EQ(displayId, mPointerDisplayIdNotified);
+ mPointerDisplayIdNotified.reset();
+ }
+
+ void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
+
+private:
+ std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
+ mCreatedControllers;
+ std::optional<int32_t> mPointerDisplayIdNotified;
+
+ std::shared_ptr<PointerControllerInterface> createPointerController(
+ ControllerType type) override {
+ std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+ EXPECT_FALSE(pc->isPointerShown());
+ mCreatedControllers.emplace_back(type, pc);
+ return pc;
+ }
+
+ void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override {
+ mPointerDisplayIdNotified = displayId;
+ }
};
TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
@@ -86,4 +191,1358 @@
}
}
+TEST_F(PointerChoreographerTest, WhenMouseIsAddedCreatesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ assertPointerControllerCreated(ControllerType::MOUSE);
+}
+
+TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ 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 create a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) {
+ // Without viewport information, PointerController will be created but viewport won't be set.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportNotSet();
+
+ // After Choreographer gets viewport, PointerController should also have viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+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)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+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)}});
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ firstDisplayPc->assertViewportSet(DISPLAY_ID);
+ ASSERT_TRUE(firstDisplayPc->isPointerShown());
+
+ // Change default mouse display. Existing PointerController should be removed and a new one
+ // should be created.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID);
+ ASSERT_TRUE(secondDisplayPc->isPointerShown());
+}
+
+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)}});
+ 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)}});
+ 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)}});
+ 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)}});
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+
+ // Set another viewport as a default mouse display ID. The mouse is moved to the other display.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ 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)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set 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 coordinates, 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)}});
+ auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+ auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+
+ // Set 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 coordinates, 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)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set 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 coordinates, 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)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ 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, MultipleMiceConnectionAndRemoval) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // A mouse is connected, and the pointer is shown.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ pc->fade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ // Add a second mouse is added, the pointer is shown again.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // One of the mice is removed, and it does not cause the mouse pointer to fade, because
+ // we have one more mouse connected.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotRemoved(pc);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // The final mouse is removed. The pointer is removed.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ pc->fade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ // Adding a touchscreen device does not unfade the mouse pointer.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ DISPLAY_ID)}});
+
+ ASSERT_FALSE(pc->isPointerShown());
+
+ // Show touches setting change does not unfade the mouse pointer.
+ mChoreographer.setShowTouchesEnabled(true);
+
+ 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);
+ 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 viewport is set for the PointerController.
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+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 viewport is unset.
+ pc->assertViewportNotSet();
+
+ // Set viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Check that the viewport is set for the PointerController.
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+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 viewport is unset.
+ pc->assertViewportNotSet();
+
+ // Set viewport which does not match the associated display of the stylus.
+ mChoreographer.setDisplayViewports(createViewports({ANOTHER_DISPLAY_ID}));
+
+ // Check that viewport is still unset.
+ pc->assertViewportNotSet();
+}
+
+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);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchpadIsAddedCreatesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ assertPointerControllerCreated(ControllerType::MOUSE);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedRemovesPointerController) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+
+ // Remove the touchpad.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForAssociatedTouchpad) {
+ // 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 | AINPUT_SOURCE_TOUCHPAD,
+ DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedTouchpad) {
+ // Without viewport information, PointerController will be created by a touchpad event
+ // but viewport won't be set.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportNotSet();
+
+ // After Choreographer gets viewport, PointerController should also have viewport.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // For a touchpad 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)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) {
+ // Set one display as a default touchpad display and create PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ firstDisplayPc->assertViewportSet(DISPLAY_ID);
+
+ // Change default mouse display. Existing PointerController should be removed.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
+ secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ assertPointerControllerCreated(ControllerType::MOUSE);
+
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotNotified();
+
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) {
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(DISPLAY_ID);
+
+ mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+ assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest,
+ WhenDefaultMouseDisplayChangesTouchpadCallsNotifyPointerDisplayIdChanged) {
+ // 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 | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ 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 touchpad event.
+ mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ assertPointerControllerRemoved(firstDisplayPc);
+
+ assertPointerControllerCreated(ControllerType::MOUSE);
+ assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
+}
+
+TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set 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(TOUCHPAD_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 coordinates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
+}
+
+TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(100, 200);
+
+ // Notify motion with fake fingers, as if it is multi-finger swipe.
+ // Check if the position of the PointerController is added to the fake finger coords.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithCoords(0, 200), WithCursorPosition(100, 200)));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCoords(0, 0, 200), WithPointerCoords(1, 100, 200),
+ WithCursorPosition(100, 200)));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(100).y(0))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCoords(0, 0, 200), WithPointerCoords(1, 100, 200),
+ WithPointerCoords(2, 200, 200), WithCursorPosition(100, 200)));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-90).y(10))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(110).y(10))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCoords(0, 10, 210), WithPointerCoords(1, 110, 210),
+ WithPointerCoords(2, 210, 210), WithCursorPosition(100, 200)));
+}
+
+TEST_F(PointerChoreographerTest,
+ AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
+ // 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 | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ANOTHER_DISPLAY_ID)}});
+ auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+ auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+
+ // Set initial positions 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(TOUCHPAD_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 coordinates, 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, DoesNotMovePointerForTouchpadSource) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(200, 300);
+
+ // Assume that pointer capture is enabled.
+ mChoreographer.notifyPointerCaptureChanged(
+ NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
+ PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+
+ // Notify motion as if pointer capture is enabled.
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+
+ // Check that there's no update on the PointerController.
+ pc->assertPosition(200, 300);
+ ASSERT_FALSE(pc->isPointerShown());
+
+ // Check x-y coordinates, displayId and cursor position are not changed.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(100, 200), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION)));
+}
+
+TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+ 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, SetsPointerIconForMouse) {
+ // Make sure there is a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertPointerIconNotSet();
+
+ // Set pointer icon for the device.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+}
+
+TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) {
+ // Make sure there is a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertPointerIconNotSet();
+
+ // Set pointer icon for wrong display id. This should be ignored.
+ ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ pc->assertPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) {
+ // Make sure there is a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertPointerIconNotSet();
+
+ // Set pointer icon for wrong device id. This should be ignored.
+ ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ pc->assertPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) {
+ // Make sure there is a PointerController.
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertCustomPointerIconNotSet();
+
+ // Set custom pointer icon for the device.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(std::make_unique<SpriteIcon>(
+ PointerIconStyle::TYPE_CUSTOM),
+ DISPLAY_ID, DEVICE_ID));
+ pc->assertCustomPointerIconSet(PointerIconStyle::TYPE_CUSTOM);
+
+ // Set custom pointer icon for wrong device id. This should be ignored.
+ ASSERT_FALSE(mChoreographer.setPointerIcon(std::make_unique<SpriteIcon>(
+ PointerIconStyle::TYPE_CUSTOM),
+ DISPLAY_ID, SECOND_DEVICE_ID));
+ pc->assertCustomPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) {
+ // Make sure there are two PointerControllers on different displays.
+ 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),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+ auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
+ auto secondMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(ANOTHER_DISPLAY_ID, secondMousePc->getDisplayId());
+
+ // Set pointer icon for one mouse.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ firstMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ secondMousePc->assertPointerIconNotSet();
+
+ // Set pointer icon for another mouse.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, ANOTHER_DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ secondMousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ firstMousePc->assertPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, SetsPointerIconForStylus) {
+ // Make sure there is a PointerController.
+ 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);
+ pc->assertPointerIconNotSet();
+
+ // Set pointer icon for the device.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+
+ // Set pointer icon for wrong device id. This should be ignored.
+ ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ pc->assertPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, SetsCustomPointerIconForStylus) {
+ // Make sure there is a PointerController.
+ 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);
+ pc->assertCustomPointerIconNotSet();
+
+ // Set custom pointer icon for the device.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(std::make_unique<SpriteIcon>(
+ PointerIconStyle::TYPE_CUSTOM),
+ DISPLAY_ID, DEVICE_ID));
+ pc->assertCustomPointerIconSet(PointerIconStyle::TYPE_CUSTOM);
+
+ // Set custom pointer icon for wrong device id. This should be ignored.
+ ASSERT_FALSE(mChoreographer.setPointerIcon(std::make_unique<SpriteIcon>(
+ PointerIconStyle::TYPE_CUSTOM),
+ DISPLAY_ID, SECOND_DEVICE_ID));
+ pc->assertCustomPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, SetsPointerIconForTwoStyluses) {
+ // Make sure there are two StylusPointerControllers. They can be on a same display.
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ 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 firstStylusPc = assertPointerControllerCreated(ControllerType::STYLUS);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto secondStylusPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Set pointer icon for one stylus.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ firstStylusPc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ secondStylusPc->assertPointerIconNotSet();
+
+ // Set pointer icon for another stylus.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ secondStylusPc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ firstStylusPc->assertPointerIconNotSet();
+}
+
+TEST_F(PointerChoreographerTest, SetsPointerIconForMouseAndStylus) {
+ // Make sure there are PointerControllers for a mouse and a stylus.
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ADISPLAY_ID_NONE)
+ .build());
+ auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto stylusPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ // Set pointer icon for the mouse.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, DEVICE_ID));
+ mousePc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ stylusPc->assertPointerIconNotSet();
+
+ // Set pointer icon for the stylus.
+ ASSERT_TRUE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID,
+ SECOND_DEVICE_ID));
+ stylusPc->assertPointerIconSet(PointerIconStyle::TYPE_TEXT);
+ mousePc->assertPointerIconNotSet();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index ee6ff53..a3e8eaf 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -18,6 +18,7 @@
#include <cmath>
#include <compare>
+#include <ios>
#include <android-base/stringprintf.h>
#include <android/input.h>
@@ -464,9 +465,144 @@
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;
+/// Pointer ids matcher
+class WithPointerIdsMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithPointerIdsMatcher(std::set<int32_t> pointerIds) : mPointerIds(pointerIds) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+ std::set<int32_t> actualPointerIds;
+ for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) {
+ const PointerProperties* properties = event.getPointerProperties(pointerIndex);
+ actualPointerIds.insert(properties->id);
+ }
+
+ if (mPointerIds != actualPointerIds) {
+ *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got "
+ << dumpSet(actualPointerIds);
+ return false;
+ }
+ return true;
+ }
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ std::set<int32_t> actualPointerIds;
+ for (const PointerProperties& properties : event.pointerProperties) {
+ actualPointerIds.insert(properties.id);
+ }
+
+ if (mPointerIds != actualPointerIds) {
+ *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got "
+ << dumpSet(actualPointerIds);
+ return false;
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with pointer ids " << dumpSet(mPointerIds); }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer ids"; }
+
+private:
+ const std::set<int32_t> mPointerIds;
+};
+
+inline WithPointerIdsMatcher WithPointerIds(const std::set<int32_t /*id*/>& pointerIds) {
+ return WithPointerIdsMatcher(pointerIds);
+}
+
+/// 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);
+}
+
+/// EventId
+class WithEventIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithEventIdMatcher(int32_t eventId) : mEventId(eventId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mEventId == args.id;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mEventId == args.id;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mEventId == event.getId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with eventId 0x" << std::hex << mEventId; }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "with eventId not equal to 0x" << std::hex << mEventId;
+ }
+
+private:
+ const int32_t mEventId;
+};
+
+inline WithEventIdMatcher WithEventId(int32_t eventId) {
+ return WithEventIdMatcher(eventId);
+}
+
+/// EventIdSource
+class WithEventIdSourceMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithEventIdSourceMatcher(IdGenerator::Source eventIdSource)
+ : mEventIdSource(eventIdSource) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mEventIdSource == IdGenerator::getSource(args.id);
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mEventIdSource == IdGenerator::getSource(args.id);
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mEventIdSource == IdGenerator::getSource(event.getId());
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with eventId from source 0x" << std::hex << ftl::to_underlying(mEventIdSource);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong event from source"; }
+
+private:
+ const IdGenerator::Source mEventIdSource;
+};
+
+inline WithEventIdSourceMatcher WithEventIdSource(IdGenerator::Source eventIdSource) {
+ return WithEventIdSourceMatcher(eventIdSource);
}
MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
@@ -543,6 +679,24 @@
return argPressure == pressure;
}
+MATCHER_P(WithSize, size, "MotionEvent with specified size") {
+ const auto argSize = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE);
+ *result_listener << "expected size " << size << ", but got " << argSize;
+ return argSize == size;
+}
+
+MATCHER_P(WithOrientation, orientation, "MotionEvent with specified orientation") {
+ const auto argOrientation = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ *result_listener << "expected orientation " << orientation << ", but got " << argOrientation;
+ return argOrientation == orientation;
+}
+
+MATCHER_P(WithDistance, distance, "MotionEvent with specified distance") {
+ const auto argDistance = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_DISTANCE);
+ *result_listener << "expected distance " << distance << ", but got " << argDistance;
+ return argDistance == distance;
+}
+
MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
@@ -586,6 +740,12 @@
return arg.buttonState == buttons;
}
+MATCHER_P(WithMetaState, metaState, "InputEvent with specified meta state") {
+ *result_listener << "expected meta state 0x" << std::hex << metaState << ", but got 0x"
+ << arg.metaState;
+ return arg.metaState == metaState;
+}
+
MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
*result_listener << "expected action button " << actionButton << ", but got "
<< arg.actionButton;
@@ -608,4 +768,16 @@
return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
}
+MATCHER_P(WithPolicyFlags, policyFlags, "InputEvent with specified policy flags") {
+ *result_listener << "expected policy flags 0x" << std::hex << policyFlags << ", but got 0x"
+ << arg.policyFlags;
+ return arg.policyFlags == static_cast<uint32_t>(policyFlags);
+}
+
+MATCHER_P(WithEdgeFlags, edgeFlags, "InputEvent with specified edge flags") {
+ *result_listener << "expected edge flags 0x" << std::hex << edgeFlags << ", but got 0x"
+ << arg.edgeFlags;
+ return arg.edgeFlags == edgeFlags;
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 6203a1d..4be1e8c 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <com_android_input_flags.h>
#include <thread>
#include "FakePointerController.h"
#include "InputMapperTest.h"
@@ -36,11 +37,19 @@
constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS;
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
+constexpr auto HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
+constexpr auto HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
+constexpr int32_t DISPLAY_ID = 0;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+
+namespace input_flags = com::android::input::flags;
/**
* Unit tests for TouchpadInputMapper.
*/
-class TouchpadInputMapperTest : public InputMapperUnitTest {
+class TouchpadInputMapperTestBase : public InputMapperUnitTest {
protected:
void SetUp() override {
InputMapperUnitTest::SetUp();
@@ -100,10 +109,19 @@
*outValue = 0;
return OK;
});
+ createDevice();
mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
}
};
+class TouchpadInputMapperTest : public TouchpadInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ TouchpadInputMapperTestBase::SetUp();
+ }
+};
+
/**
* Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is
* generated when hovering stops. Currently, it is not.
@@ -135,12 +153,83 @@
setScanCodeState(KeyState::UP, {BTN_LEFT});
args += process(EV_SYN, SYN_REPORT, 0);
ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)),
VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER))));
+
+ // Liftoff
+ args.clear();
+ args += process(EV_ABS, ABS_MT_PRESSURE, 0);
+ args += process(EV_ABS, ABS_MT_TRACKING_ID, -1);
+ args += process(EV_KEY, BTN_TOUCH, 0);
+ setScanCodeState(KeyState::UP, {BTN_TOOL_FINGER});
+ args += process(EV_KEY, BTN_TOOL_FINGER, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args, testing::IsEmpty());
+}
+
+class TouchpadInputMapperTestWithChoreographer : public TouchpadInputMapperTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ TouchpadInputMapperTestBase::SetUp();
+ }
+};
+
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+/**
+ * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is
+ * generated when hovering stops. Currently, it is not.
+ * In the current implementation, HOVER_MOVE and ACTION_DOWN events are not sent out right away,
+ * but only after the button is released.
+ */
+TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) {
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+
+ std::list<NotifyArgs> args;
+
+ args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
+ args += process(EV_KEY, BTN_TOUCH, 1);
+ setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
+ args += process(EV_KEY, BTN_TOOL_FINGER, 1);
+ args += process(EV_ABS, ABS_MT_POSITION_X, 50);
+ args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
+ args += process(EV_ABS, ABS_MT_PRESSURE, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ // Without this sleep, the test fails.
+ // TODO(b/284133337): Figure out whether this can be removed
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+
+ args += process(EV_KEY, BTN_LEFT, 1);
+ setScanCodeState(KeyState::DOWN, {BTN_LEFT});
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ args += process(EV_KEY, BTN_LEFT, 0);
+ setScanCodeState(KeyState::UP, {BTN_LEFT});
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER))));
// Liftoff
args.clear();
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..dc5a213
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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) {}
+
+ std::optional<NotifyMotionArgs> nextMotion() {
+ NotifyMotionArgs args = generateFuzzedMotionArgs(mIdGenerator, mFdp, MAX_RANDOM_DISPLAYS);
+ auto [it, _] = mVerifiers.emplace(args.displayId, "Fuzz Verifier");
+ InputVerifier& verifier = it->second;
+ const Result<void> result =
+ verifier.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;
+
+ std::map<int32_t /*displayId*/, InputVerifier> mVerifiers;
+};
+
+void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) {
+ 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.setIsWallpaper(fdp.ConsumeBool());
+ window.setVisible(fdp.ConsumeBool());
+ window.setPreventSplitting(fdp.ConsumeBool());
+ const bool isTrustedOverlay = fdp.ConsumeBool();
+ window.setTrustedOverlay(isTrustedOverlay);
+ if (isTrustedOverlay) {
+ window.setSpy(fdp.ConsumeBool());
+ } else {
+ window.setSpy(false);
+ }
+}
+
+} // 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, windowName, displayId);
+
+ scrambleWindow(fdp, *window);
+ 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);
+ }
+ },
+ // Change flags or move some of the existing windows
+ [&]() -> void {
+ for (auto& window : windows) {
+ if (fdp.ConsumeBool()) {
+ scrambleWindow(fdp, *window);
+ }
+ }
+ },
+ })();
+}
+
+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..81c570d 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -275,6 +275,9 @@
void clearSpots() override {}
int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
void setDisplayViewport(const DisplayViewport& displayViewport) override {}
+ void updatePointerIcon(PointerIconStyle iconId) override {}
+ void setCustomPointerIcon(const SpriteIcon& icon) override {}
+ std::string dump() override { return ""; }
};
class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
@@ -309,6 +312,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 +367,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..bd2b10a
--- /dev/null
+++ b/services/powermanager/WorkDuration.cpp
@@ -0,0 +1,52 @@
+/**
+ * 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)
+ : timestampNanos(0),
+ actualTotalDurationNanos(totalDurationNanos),
+ workPeriodStartTimestampNanos(startTimestampNanos),
+ 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..26a575f
--- /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 timestampNanos;
+ int64_t actualTotalDurationNanos;
+ int64_t workPeriodStartTimestampNanos;
+ int64_t actualCpuDurationNanos;
+ int64_t actualGpuDurationNanos;
+};
+
+} // namespace android::os
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 641ba9f..3d2cf29 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -29,9 +29,12 @@
#include <thread>
using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::ChannelConfig;
using aidl::android::hardware::power::IPower;
using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::Mode;
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionTag;
using android::binder::Status;
using namespace android;
@@ -53,6 +56,14 @@
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, SessionTag tag, SessionConfig* config,
+ std::shared_ptr<IPowerHintSession>* _aidl_return),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
+ (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 11c56a8..0dd4dd6 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,6 +7,18 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "sensorservice_flags",
+ package: "com.android.frameworks.sensorservice.flags",
+ srcs: ["senserservice_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "sensorservice_flags_c_lib",
+ aconfig_declarations: "dynamic_sensors_flags",
+ host_supported: true,
+}
+
cc_library {
name: "libsensorservice",
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 44d0d70..85043c9 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -91,7 +91,7 @@
std::map<String16, int> SensorService::sPackageTargetVersion;
Mutex SensorService::sPackageTargetVersionLock;
String16 SensorService::sSensorInterfaceDescriptorPrefix =
- String16("android.frameworks.sensorservice@");
+ String16("android.frameworks.sensorservice");
AppOpsManager SensorService::sAppOpsManager;
std::atomic_uint64_t SensorService::curProxCallbackSeq(0);
std::atomic_uint64_t SensorService::completedCallbackSeq(0);
@@ -2295,10 +2295,12 @@
}
int SensorService::getTargetSdkVersion(const String16& opPackageName) {
- // Don't query the SDK version for the ISensorManager descriptor as it doesn't have one. This
- // descriptor tends to be used for VNDK clients, but can technically be set by anyone so don't
- // give it elevated privileges.
- if (opPackageName.startsWith(sSensorInterfaceDescriptorPrefix)) {
+ // Don't query the SDK version for the ISensorManager descriptor as it
+ // doesn't have one. This descriptor tends to be used for VNDK clients, but
+ // can technically be set by anyone so don't give it elevated privileges.
+ bool isVNDK = opPackageName.startsWith(sSensorInterfaceDescriptorPrefix) &&
+ opPackageName.contains(String16("@"));
+ if (isVNDK) {
return -1;
}
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 2b6ea7c..b6acc8a 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -188,8 +188,16 @@
}
::android::SensorManager& SensorManagerAidl::getInternalManager() {
- return ::android::SensorManager::getInstanceForPackage(
- String16(ISensorManager::descriptor));
+ int32_t version;
+ ndk::ScopedAStatus status = getInterfaceVersion(&version);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to get interface version with error: "
+ << status.getDescription();
+ version = -1;
+ }
+ String16 packageName = String16(ISensorManager::descriptor);
+ packageName += String16("@") + String16(std::to_string(version).c_str());
+ return ::android::SensorManager::getInstanceForPackage(packageName);
}
/* One global looper for all event queues created from this SensorManager. */
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
new file mode 100644
index 0000000..a3bd0ee
--- /dev/null
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -0,0 +1,15 @@
+package: "com.android.frameworks.sensorservice.flags"
+
+flag {
+ name: "dynamic_sensor_hal_reconnect_handling"
+ namespace: "sensors"
+ description: "This flag controls if the dynamic sensor data will be clean up after HAL is disconnected."
+ bug: "307782607"
+}
+
+flag {
+ name: "sensor_device_on_dynamic_sensor_disconnected"
+ namespace: "sensors"
+ description: "This flag controls if the callback onDynamicSensorsDisconnected is implemented or not."
+ bug: "316958439"
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9d0f285..0989863 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -93,6 +93,7 @@
"libscheduler",
"libserviceutils",
"libshaders",
+ "libsurfaceflinger_common",
"libtimestats",
"libtonemap",
"libsurfaceflingerflags",
@@ -175,7 +176,6 @@
"FrontEnd/LayerLifecycleManager.cpp",
"FrontEnd/RequestedLayerState.cpp",
"FrontEnd/TransactionHandler.cpp",
- "FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
@@ -245,6 +245,7 @@
],
static_libs: [
"android.frameworks.displayservice@1.0",
+ "libc++fs",
"libdisplayservicehidl",
"libserviceutils",
],
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 370e4b6..ae2f2db 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -37,6 +37,7 @@
"libSurfaceFlingerProp",
"libui",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
"liblayers_proto",
@@ -60,10 +61,8 @@
],
}
-cc_library {
- name: "libcompositionengine",
- defaults: ["libcompositionengine_defaults"],
- static_libs: ["libsurfaceflingerflags"],
+filegroup {
+ name: "libcompositionengine_sources",
srcs: [
"src/planner/CachedSet.cpp",
"src/planner/Flattener.cpp",
@@ -86,8 +85,23 @@
"src/OutputLayerCompositionState.cpp",
"src/RenderSurface.cpp",
],
+}
+
+cc_library {
+ name: "libcompositionengine",
+ defaults: ["libcompositionengine_defaults"],
+ static_libs: [
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
+ ],
+ srcs: [
+ ":libcompositionengine_sources",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
}
cc_library {
@@ -108,8 +122,12 @@
"libgtest",
"libgmock",
"libcompositionengine",
+ "libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -117,8 +135,13 @@
cc_test {
name: "libcompositionengine_test",
test_suites: ["device-tests"],
+ include_dirs: [
+ "frameworks/native/services/surfaceflinger/common/include",
+ "frameworks/native/services/surfaceflinger/tests/unittests",
+ ],
defaults: ["libcompositionengine_defaults"],
srcs: [
+ ":libcompositionengine_sources",
"tests/planner/CachedSetTest.cpp",
"tests/planner/FlattenerTest.cpp",
"tests/planner/LayerStateTest.cpp",
@@ -137,18 +160,19 @@
"tests/RenderSurfaceTest.cpp",
],
static_libs: [
- "libcompositionengine",
"libcompositionengine_mocks",
"libgui_mocks",
"librenderengine_mocks",
"libgmock",
"libgtest",
+ "libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
- // For some reason, libvulkan isn't picked up from librenderengine
- // Probably ASAN related?
shared_libs: [
+ // For some reason, libvulkan isn't picked up from librenderengine
+ // Probably ASAN related?
"libvulkan",
+ "server_configurable_flags",
],
sanitize: {
hwaddress: true,
diff --git a/services/surfaceflinger/CompositionEngine/AndroidTest.xml b/services/surfaceflinger/CompositionEngine/AndroidTest.xml
new file mode 100644
index 0000000..94e92f0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for libcompositionengine_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="libcompositionengine_test->/data/local/tmp/libcompositionengine_test" />
+ </target_preparer>
+
+ <!--
+ Disable SELinux so that crashes in the test suite produces symbolized stack traces.
+ -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libcompositionengine_test" />
+ </test>
+</configuration>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 1a8644e..18a96f4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -24,6 +24,7 @@
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputColorSetting.h>
#include <math/mat4.h>
+#include <scheduler/interface/ICompositor.h>
#include <ui/FenceTime.h>
#include <ui/Transform.h>
@@ -89,14 +90,14 @@
// If set, causes the dirty regions to flash with the delay
std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
- // Optional.
- // The earliest time to send the present command to the HAL.
- std::optional<std::chrono::steady_clock::time_point> earliestPresentTime;
+ scheduler::FrameTargets frameTargets;
- // The expected time for the next present
- nsecs_t expectedPresentTime{0};
+ // The frameInterval for the next present
+ // TODO (b/315371484): Calculate per display and store on `FrameTarget`.
+ Fps frameInterval;
// If set, a frame has been scheduled for that time.
+ // TODO (b/255601557): Calculate per display.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
std::vector<BorderRenderInfo> borderInfoList;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 5e84be1..c71c517 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -40,6 +40,9 @@
// True if the display is secure
virtual bool isSecure() const = 0;
+ // Sets the secure flag for the display
+ virtual void setSecure(bool secure) = 0;
+
// True if the display is virtual
virtual bool isVirtual() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index 9c80cac..a74c5fe 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -36,7 +36,7 @@
namespace compositionengine {
/**
- * Encapsulates all the state and functionality for how colors should be
+ * Encapsulates all the states and functionality for how colors should be
* transformed for a display
*/
class DisplayColorProfile {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 98c4af4..6e60839 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -42,6 +42,10 @@
// True if this display should be considered secure
bool isSecure = false;
+ // True if this display should be considered protected, as in this display should render DRM
+ // content.
+ bool isProtected = false;
+
// Optional pointer to the power advisor interface, if one is needed for
// this display.
Hwc2::PowerAdvisor* powerAdvisor = nullptr;
@@ -73,6 +77,11 @@
return *this;
}
+ DisplayCreationArgsBuilder& setIsProtected(bool isProtected) {
+ mArgs.isProtected = isProtected;
+ return *this;
+ }
+
DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
mArgs.powerAdvisor = powerAdvisor;
return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index ca86f4c..643b458 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -60,7 +60,7 @@
//
// advanceFrame must be followed by a call to onFrameCommitted before
// advanceFrame may be called again.
- virtual status_t advanceFrame() = 0;
+ virtual status_t advanceFrame(float hdrSdrRatio) = 0;
// onFrameCommitted is called after the frame has been committed to the
// hardware composer. The surface collects the release fence for this
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index ccff1ec..a1d6132 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -97,7 +97,7 @@
const bool isSecure;
// If set to true, the target buffer has protected content support.
- const bool supportsProtectedContent;
+ const bool isProtected;
// Viewport of the target being rendered to. This is used to determine
// the shadow light position.
@@ -167,8 +167,7 @@
static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
const LayerFE::ClientCompositionTargetSettings& rhs) {
return lhs.clip.hasSameRects(rhs.clip) && lhs.needsFiltering == rhs.needsFiltering &&
- lhs.isSecure == rhs.isSecure &&
- lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
+ lhs.isSecure == rhs.isSecure && lhs.isProtected == rhs.isProtected &&
lhs.viewport == rhs.viewport && lhs.dataspace == rhs.dataspace &&
lhs.realContentIsVisible == rhs.realContentIsVisible &&
lhs.clearContent == rhs.clearContent;
@@ -189,7 +188,7 @@
PrintTo(settings.clip, os);
*os << "\n .needsFiltering = " << settings.needsFiltering;
*os << "\n .isSecure = " << settings.isSecure;
- *os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
+ *os << "\n .isProtected = " << settings.isProtected;
*os << "\n .viewport = ";
PrintTo(settings.viewport, os);
*os << "\n .dataspace = ";
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 370c7cf..f1d6f52 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -26,6 +26,7 @@
#include <vector>
#include <compositionengine/LayerFE.h>
+#include <ftl/future.h>
#include <renderengine/LayerSettings.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
@@ -61,7 +62,7 @@
} // namespace impl
/**
- * Encapsulates all the state involved with composing layers for an output
+ * Encapsulates all the states involved with composing layers for an output
*/
class Output {
public:
@@ -263,8 +264,15 @@
// Prepare the output, updating the OutputLayers used in the output
virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
- // Presents the output, finalizing all composition details
- virtual void present(const CompositionRefreshArgs&) = 0;
+ // Presents the output, finalizing all composition details. This may happen
+ // asynchronously, in which case the returned future must be waited upon.
+ virtual ftl::Future<std::monostate> present(const CompositionRefreshArgs&) = 0;
+
+ // Whether this output can be presented from another thread.
+ virtual bool supportsOffloadPresent() const = 0;
+
+ // Make the next call to `present` run asynchronously.
+ virtual void offloadPresentNextFrame() = 0;
// Enables predicting composition strategy to run client composition earlier
virtual void setPredictCompositionStrategy(bool) = 0;
@@ -298,14 +306,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/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index 5854674..02cea0d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -86,7 +86,7 @@
// Queues the drawn buffer for consumption by HWC. readyFence is the fence
// which will fire when the buffer is ready for consumption.
- virtual void queueBuffer(base::unique_fd readyFence) = 0;
+ virtual void queueBuffer(base::unique_fd readyFence, float hdrSdrRatio) = 0;
// Called after the HWC calls are made to present the display
virtual void onPresentDisplayCompleted() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 6cf1d68..c53b461 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -59,9 +59,10 @@
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;
+ bool supportsOffloadPresent() const override;
// compositionengine::Display overrides
DisplayId getId() const override;
@@ -73,6 +74,7 @@
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
void applyDisplayBrightness(const bool applyImmediately) override;
+ void setSecure(bool secure) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 229a657..911d67b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -80,7 +80,9 @@
void setReleasedLayers(ReleasedLayers&&) override;
void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
- void present(const CompositionRefreshArgs&) override;
+ ftl::Future<std::monostate> present(const CompositionRefreshArgs&) override;
+ bool supportsOffloadPresent() const override { return false; }
+ void offloadPresentNextFrame() override;
void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
@@ -102,7 +104,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;
@@ -121,6 +123,7 @@
virtual std::future<bool> chooseCompositionStrategyAsync(
std::optional<android::HWComposer::DeviceRequestedChanges>*);
virtual void resetCompositionStrategy();
+ virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync();
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -133,8 +136,9 @@
};
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
- compositionengine::Output::FrameFences presentAndGetFrameFences() override;
- virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const;
+ compositionengine::Output::FrameFences presentFrame() override;
+ virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings(
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer) const;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<LayerFE*>& outLayerFEs) override;
@@ -164,6 +168,8 @@
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
compositionengine::Output::ColorProfile pickColorProfile(
const compositionengine::CompositionRefreshArgs&) const;
+ void updateHwcAsyncWorker();
+ float getHdrSdrRatio(const std::shared_ptr<renderengine::ExternalTexture>& buffer) const;
std::string mName;
std::string mNamePlusId;
@@ -177,6 +183,9 @@
std::unique_ptr<planner::Planner> mPlanner;
std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
+ bool mPredictCompositionStrategy = false;
+ bool mOffloadPresent = false;
+
// Whether the content must be recomposed this frame.
bool mMustRecompose = false;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 6cb1e7e..6b1c318 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -53,6 +53,9 @@
// If false, this output is not considered secure
bool isSecure{false};
+ // If false, this output is not considered protected
+ bool isProtected{false};
+
// If true, the current frame on this output uses client composition
bool usesClientComposition{false};
@@ -127,6 +130,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/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 7b0af3a..6c419da 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -90,7 +90,7 @@
// The source crop for this layer on this output
FloatRect sourceCrop;
- // The buffer transform to use for this layer o on this output.
+ // The buffer transform to use for this layer on this output.
Hwc2::Transform bufferTransform{static_cast<Hwc2::Transform>(0)};
// The dataspace for this layer
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 1c14a43..202145e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -60,7 +60,7 @@
void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
base::unique_fd* bufferFence) override;
- void queueBuffer(base::unique_fd readyFence) override;
+ void queueBuffer(base::unique_fd readyFence, float hdrSdrRatio) override;
void onPresentDisplayCompleted() override;
bool supportsCompositionStrategyPrediction() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index ce2b96f..1f241b0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -227,6 +227,7 @@
// Returns the bit-set of differing fields between this LayerState and another LayerState.
// This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers.
ftl::Flags<LayerStateField> getDifferingFields(const LayerState& other) const;
+ bool isSourceCropSizeEqual(const LayerState& other) const;
compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
int32_t getId() const { return mId.get(); }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 7e99ec2..46cb95e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -33,6 +33,7 @@
MOCK_CONST_METHOD0(getId, DisplayId());
MOCK_CONST_METHOD0(isSecure, bool());
+ MOCK_METHOD1(setSecure, void(bool));
MOCK_CONST_METHOD0(isVirtual, bool());
MOCK_CONST_METHOD0(getPreferredBootHwcConfigId, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
index 168e433..08d8ff7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
@@ -30,7 +30,7 @@
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD1(prepareFrame, status_t(CompositionType compositionType));
- MOCK_METHOD0(advanceFrame, status_t());
+ MOCK_METHOD((status_t), advanceFrame, (float), (override));
MOCK_METHOD0(onFrameCommitted, void());
MOCK_CONST_METHOD1(dumpAsString, void(String8& result));
MOCK_METHOD1(resizeBuffers, void(const ui::Size&));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index a56fc79..95ea3a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -80,7 +80,10 @@
MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
- MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(present,
+ ftl::Future<std::monostate>(const compositionengine::CompositionRefreshArgs&));
+ MOCK_CONST_METHOD0(supportsOffloadPresent, bool());
+ MOCK_METHOD(void, offloadPresentNextFrame, ());
MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
MOCK_METHOD2(rebuildLayerStacks,
@@ -118,9 +121,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/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index af8d4bc..c35fd3f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -40,7 +40,7 @@
MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
MOCK_METHOD2(prepareFrame, void(bool, bool));
MOCK_METHOD1(dequeueBuffer, std::shared_ptr<renderengine::ExternalTexture>(base::unique_fd*));
- MOCK_METHOD1(queueBuffer, void(base::unique_fd));
+ MOCK_METHOD(void, queueBuffer, (base::unique_fd, float), (override));
MOCK_METHOD0(onPresentDisplayCompleted, void());
MOCK_CONST_METHOD1(dump, void(std::string& result));
MOCK_CONST_METHOD0(supportsCompositionStrategyPrediction, bool());
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 15fadbc..d87eae3 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -20,6 +20,7 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/impl/Display.h>
+#include <ui/DisplayMap.h>
#include <renderengine/RenderEngine.h>
#include <utils/Trace.h>
@@ -88,6 +89,44 @@
return mRefreshStartTime;
}
+namespace {
+void offloadOutputs(Outputs& outputs) {
+ if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) {
+ return;
+ }
+
+ ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload;
+ for (const auto& output : outputs) {
+ if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) {
+ // Not HWC-enabled, so it is always client-composited. No need to offload.
+ continue;
+ }
+ if (!output->getState().isEnabled) {
+ continue;
+ }
+
+ // Only run present in multiple threads if all HWC-enabled displays
+ // being refreshed support it.
+ if (!output->supportsOffloadPresent()) {
+ return;
+ }
+ outputsToOffload.push_back(output.get());
+ }
+
+ if (outputsToOffload.size() < 2) {
+ return;
+ }
+
+ // Leave the last eligible display on the main thread, which will
+ // allow it to run concurrently without an extra thread hop.
+ outputsToOffload.pop_back();
+
+ for (compositionengine::Output* output : outputsToOffload) {
+ output->offloadPresentNextFrame();
+ }
+}
+} // namespace
+
void CompositionEngine::present(CompositionRefreshArgs& args) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -105,14 +144,27 @@
}
}
+ // Offloading the HWC call for `present` allows us to simultaneously call it
+ // on multiple displays. This is desirable because these calls block and can
+ // be slow.
+ offloadOutputs(args.outputs);
+
+ ui::DisplayVector<ftl::Future<std::monostate>> presentFutures;
for (const auto& output : args.outputs) {
- output->present(args);
+ presentFutures.push_back(output->present(args));
+ }
+
+ {
+ ATRACE_NAME("Waiting on HWC");
+ for (auto& future : presentFutures) {
+ // TODO(b/185536303): Call ftl::Future::wait() once it exists, since
+ // we do not need the return value of get().
+ future.get();
+ }
}
}
void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
- std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*>
- uniqueVisibleLayers;
for (const auto& output : args.outputs) {
for (auto* layer : output->getOutputLayersOrderedByZ()) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index f2acfc9..d907bf5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -57,6 +57,7 @@
mId = args.id;
mPowerAdvisor = args.powerAdvisor;
editState().isSecure = args.isSecure;
+ editState().isProtected = args.isProtected;
editState().displaySpace.setBounds(args.pixels);
setName(args.name);
}
@@ -73,6 +74,10 @@
return getState().isSecure;
}
+void Display::setSecure(bool secure) {
+ editState().isSecure = secure;
+}
+
bool Display::isVirtual() const {
return VirtualDisplayId::tryCast(mId).has_value();
}
@@ -245,7 +250,6 @@
}
// Get any composition changes requested by the HWC device, and apply them.
- std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
const bool requiresClientComposition = anyLayersRequireClientComposition();
@@ -255,10 +259,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 +366,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) {
@@ -430,4 +434,13 @@
impl::Output::finishFrame(std::move(result));
}
+bool Display::supportsOffloadPresent() const {
+ if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ const auto& hwc = getCompositionEngine().getHwComposer();
+ return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
+ }
+
+ return false;
+}
+
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index fa3733b..1c2f6cb 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -28,8 +28,11 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
+#include <ftl/algorithm.h>
#include <ftl/future.h>
#include <gui/TraceUtils.h>
+#include <scheduler/FrameTargeter.h>
+#include <scheduler/Time.h>
#include <optional>
#include <thread>
@@ -427,8 +430,30 @@
uncacheBuffers(refreshArgs.bufferIdsToUncache);
}
-void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
+ftl::Future<std::monostate> Output::present(
+ const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
+ return ftl::Optional(getDisplayId())
+ .and_then(PhysicalDisplayId::tryCast)
+ .and_then([&refreshArgs](PhysicalDisplayId id) {
+ return refreshArgs.frameTargets.get(id);
+ })
+ .transform([](const auto& frameTargetPtr) {
+ return frameTargetPtr.get()->expectedPresentTime();
+ })
+ .transform([](TimePoint expectedPresentTime) {
+ return base::StringPrintf(" vsyncIn %.2fms",
+ ticks<std::milli, float>(expectedPresentTime -
+ TimePoint::now()));
+ })
+ .or_else([] {
+ // There is no vsync for this output.
+ return std::make_optional(std::string());
+ })
+ .value();
+ };
+ ATRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(),
+ stringifyExpectedPresentTime().c_str());
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
@@ -448,8 +473,26 @@
devOptRepaintFlash(refreshArgs);
finishFrame(std::move(result));
- postFramebuffer();
+ ftl::Future<std::monostate> future;
+ if (mOffloadPresent) {
+ future = presentFrameAndReleaseLayersAsync();
+
+ // Only offload for this frame. The next frame will determine whether it
+ // needs to be offloaded. Leave the HwcAsyncWorker in place. For one thing,
+ // it is currently presenting. Further, it may be needed next frame, and
+ // we don't want to churn.
+ mOffloadPresent = false;
+ } else {
+ presentFrameAndReleaseLayers();
+ future = ftl::yield<std::monostate>({});
+ }
renderCachedSets(refreshArgs);
+ return future;
+}
+
+void Output::offloadPresentNextFrame() {
+ mOffloadPresent = true;
+ updateHwcAsyncWorker();
}
void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
@@ -834,8 +877,15 @@
return;
}
- editState().earliestPresentTime = refreshArgs.earliestPresentTime;
- editState().expectedPresentTime = refreshArgs.expectedPresentTime;
+ if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId())
+ .and_then(PhysicalDisplayId::tryCast)
+ .and_then([&refreshArgs](PhysicalDisplayId id) {
+ return refreshArgs.frameTargets.get(id);
+ })) {
+ editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime();
+ editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns();
+ }
+ editState().frameInterval = refreshArgs.frameInterval;
editState().powerCallback = refreshArgs.powerCallback;
compositionengine::OutputLayer* peekThroughLayer = nullptr;
@@ -1083,6 +1133,14 @@
finishPrepareFrame();
}
+ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync() {
+ return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([&]() {
+ presentFrameAndReleaseLayers();
+ return true;
+ })))
+ .then([](bool) { return std::monostate{}; });
+}
+
std::future<bool> Output::chooseCompositionStrategyAsync(
std::optional<android::HWComposer::DeviceRequestedChanges>* changes) {
return mHwComposerAsyncWorker->send(
@@ -1148,11 +1206,11 @@
updateProtectedContentState();
dequeueRenderBuffer(&bufferFence, &buffer);
static_cast<void>(composeSurfaces(dirtyRegion, buffer, bufferFence));
- mRenderSurface->queueBuffer(base::unique_fd());
+ mRenderSurface->queueBuffer(base::unique_fd(), getHdrSdrRatio(buffer));
}
}
- postFramebuffer();
+ presentFrameAndReleaseLayers();
std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
@@ -1196,7 +1254,7 @@
std::make_unique<FenceTime>(sp<Fence>::make(dup(optReadyFence->get()))));
}
// swap buffers (presentation)
- mRenderSurface->queueBuffer(std::move(*optReadyFence));
+ mRenderSurface->queueBuffer(std::move(*optReadyFence), getHdrSdrRatio(buffer));
}
void Output::updateProtectedContentState() {
@@ -1204,10 +1262,18 @@
auto& renderEngine = getCompositionEngine().getRenderEngine();
const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
- // If we the display is secure, protected content support is enabled, and at
- // least one layer has protected content, we need to use a secure back
- // buffer.
- if (outputState.isSecure && supportsProtectedContent) {
+ bool isProtected;
+ if (FlagManager::getInstance().display_protected()) {
+ isProtected = outputState.isProtected;
+ } else {
+ isProtected = outputState.isSecure;
+ }
+
+ // We need to set the render surface as protected (DRM) if all the following conditions are met:
+ // 1. The display is protected (in legacy, check if the display is secure)
+ // 2. Protected content is supported
+ // 3. At least one layer has protected content.
+ if (isProtected && supportsProtectedContent) {
auto layers = getOutputLayersOrderedByZ();
bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
return layer->getLayerFE().getCompositionState()->hasProtectedContent;
@@ -1262,7 +1328,7 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay =
- generateClientCompositionDisplaySettings();
+ generateClientCompositionDisplaySettings(tex);
// Generate the client composition requests for the layers on this output.
auto& renderEngine = getCompositionEngine().getRenderEngine();
@@ -1343,7 +1409,8 @@
return base::unique_fd(fence->dup());
}
-renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings() const {
+renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings(
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer) const {
const auto& outputState = getState();
renderengine::DisplaySettings clientCompositionDisplay;
@@ -1363,8 +1430,10 @@
: mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
clientCompositionDisplay.maxLuminance =
mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.targetLuminanceNits =
- outputState.clientTargetBrightness * outputState.displayBrightnessNits;
+
+ float hdrSdrRatioMultiplier = 1.0f / getHdrSdrRatio(buffer);
+ clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetBrightness *
+ outputState.displayBrightnessNits * hdrSdrRatioMultiplier;
clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage;
clientCompositionDisplay.renderIntent =
static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(
@@ -1447,12 +1516,16 @@
BlurRegionsOnly
: LayerFE::ClientCompositionTargetSettings::BlurSetting::
Enabled);
+ bool isProtected = supportsProtectedContent;
+ if (FlagManager::getInstance().display_protected()) {
+ isProtected = outputState.isProtected && supportsProtectedContent;
+ }
compositionengine::LayerFE::ClientCompositionTargetSettings
targetSettings{.clip = clip,
.needsFiltering = layer->needsFiltering() ||
outputState.needsFiltering,
.isSecure = outputState.isSecure,
- .supportsProtectedContent = supportsProtectedContent,
+ .isProtected = isProtected,
.viewport = outputState.layerStackSpace.getContent(),
.dataspace = outputDataspace,
.realContentIsVisible = realContentIsVisible,
@@ -1509,7 +1582,7 @@
return false;
}
-void Output::postFramebuffer() {
+void Output::presentFrameAndReleaseLayers() {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
@@ -1520,7 +1593,7 @@
auto& outputState = editState();
outputState.dirtyRegion.clear();
- auto frame = presentAndGetFrameFences();
+ auto frame = presentFrame();
mRenderSurface->onPresentDisplayCompleted();
@@ -1590,7 +1663,7 @@
return true;
}
-compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
+compositionengine::Output::FrameFences Output::presentFrame() {
compositionengine::Output::FrameFences result;
if (getState().usesClientComposition) {
result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
@@ -1599,8 +1672,15 @@
}
void Output::setPredictCompositionStrategy(bool predict) {
- if (predict) {
- mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+ mPredictCompositionStrategy = predict;
+ updateHwcAsyncWorker();
+}
+
+void Output::updateHwcAsyncWorker() {
+ if (mPredictCompositionStrategy || mOffloadPresent) {
+ if (!mHwComposerAsyncWorker) {
+ mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>();
+ }
} else {
mHwComposerAsyncWorker.reset(nullptr);
}
@@ -1615,7 +1695,7 @@
uint64_t outputLayerHash = getState().outputLayerHash;
editState().lastOutputLayerHash = outputLayerHash;
- if (!getState().isEnabled || !mHwComposerAsyncWorker) {
+ if (!getState().isEnabled || !mPredictCompositionStrategy) {
ALOGV("canPredictCompositionStrategy disabled");
return false;
}
@@ -1668,5 +1748,25 @@
return mMustRecompose;
}
+float Output::getHdrSdrRatio(const std::shared_ptr<renderengine::ExternalTexture>& buffer) const {
+ if (buffer == nullptr) {
+ return 1.0f;
+ }
+
+ if (!FlagManager::getInstance().fp16_client_target()) {
+ return 1.0f;
+ }
+
+ if (getState().displayBrightnessNits < 0.0f || getState().sdrWhitePointNits <= 0.0f ||
+ buffer->getPixelFormat() != PIXEL_FORMAT_RGBA_FP16 ||
+ (static_cast<int32_t>(getState().dataspace) &
+ static_cast<int32_t>(ui::Dataspace::RANGE_MASK)) !=
+ static_cast<int32_t>(ui::Dataspace::RANGE_EXTENDED)) {
+ return 1.0f;
+ }
+
+ return getState().displayBrightnessNits / getState().sdrWhitePointNits;
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 0fe55db..c0b23d9 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -198,7 +198,7 @@
return mTexture;
}
-void RenderSurface::queueBuffer(base::unique_fd readyFence) {
+void RenderSurface::queueBuffer(base::unique_fd readyFence, float hdrSdrRatio) {
auto& state = mDisplay.getState();
if (state.usesClientComposition || state.flipClientTarget) {
@@ -241,7 +241,7 @@
}
}
- status_t result = mDisplaySurface->advanceFrame();
+ status_t result = mDisplaySurface->advanceFrame(hdrSdrRatio);
if (result != NO_ERROR) {
ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplay.getName().c_str(), result);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 579c6ba..869dda6 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -184,7 +184,7 @@
targetSettings{.clip = Region(viewport),
.needsFiltering = false,
.isSecure = outputState.isSecure,
- .supportsProtectedContent = false,
+ .isProtected = false,
.viewport = viewport,
.dataspace = outputDataspace,
.realContentIsVisible = true,
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 13b6307..91cfe5d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -20,6 +20,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/properties.h>
+#include <common/FlagManager.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
@@ -50,8 +51,19 @@
for (size_t i = 0; i < incomingLayers.size(); i++) {
// Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try
// to access destroyed OutputLayers later on.
- if (incomingLayers[i]->getId() != existingLayers[i]->getId() ||
- incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
+ if (incomingLayers[i]->getId() != existingLayers[i]->getId()) {
+ return false;
+ }
+
+ // Do not unflatten if source crop is only moved.
+ if (FlagManager::getInstance().cache_when_source_crop_layer_only_moved() &&
+ incomingLayers[i]->isSourceCropSizeEqual(*(existingLayers[i])) &&
+ incomingLayers[i]->getDifferingFields(*(existingLayers[i])) ==
+ LayerStateField::SourceCrop) {
+ continue;
+ }
+
+ if (incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) {
return false;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 8dab6ce..0e3fdbb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -76,6 +76,11 @@
return hash;
}
+bool LayerState::isSourceCropSizeEqual(const LayerState& other) const {
+ return mSourceCrop.get().getWidth() == other.mSourceCrop.get().getWidth() &&
+ mSourceCrop.get().getHeight() == other.mSourceCrop.get().getHeight();
+}
+
ftl::Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const {
ftl::Flags<LayerStateField> differences;
auto myFields = getNonUniqueFields();
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 60ed660..da578e2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -14,18 +14,25 @@
* limitations under the License.
*/
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/test/FlagUtils.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/CompositionEngine.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
#include <compositionengine/mock/OutputLayer.h>
+#include <ftl/future.h>
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>
#include "MockHWComposer.h"
#include "TimeStats/TimeStats.h"
+#include <variant>
+
+using namespace com::android::graphics::surfaceflinger;
+
namespace android::compositionengine {
namespace {
@@ -107,10 +114,17 @@
EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
+ // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce
+ // calls to getDisplayId, which are not relevant to this test.
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
+
// The last step is to actually present each output.
- EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
- EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
- EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)));
+ EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
mEngine.present(mRefreshArgs);
@@ -260,5 +274,214 @@
EXPECT_TRUE(mEngine.needsAnotherUpdate());
}
+struct CompositionEngineOffloadTest : public testing::Test {
+ impl::CompositionEngine mEngine;
+ CompositionRefreshArgs mRefreshArgs;
+
+ std::shared_ptr<mock::Output> mDisplay1{std::make_shared<StrictMock<mock::Output>>()};
+ std::shared_ptr<mock::Output> mDisplay2{std::make_shared<StrictMock<mock::Output>>()};
+ std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()};
+ std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()};
+
+ static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u);
+ static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u);
+ static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u};
+ static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u};
+
+ std::array<impl::OutputCompositionState, 4> mOutputStates;
+
+ void SetUp() override {
+ EXPECT_CALL(*mDisplay1, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+ EXPECT_CALL(*mDisplay2, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+ EXPECT_CALL(*mVirtualDisplay, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId)));
+ EXPECT_CALL(*mHalVirtualDisplay, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId)));
+
+ // Most tests will depend on the outputs being enabled.
+ for (auto& state : mOutputStates) {
+ state.isEnabled = true;
+ }
+
+ EXPECT_CALL(*mDisplay1, getState).WillRepeatedly(ReturnRef(mOutputStates[0]));
+ EXPECT_CALL(*mDisplay2, getState).WillRepeatedly(ReturnRef(mOutputStates[1]));
+ EXPECT_CALL(*mVirtualDisplay, getState).WillRepeatedly(ReturnRef(mOutputStates[2]));
+ EXPECT_CALL(*mHalVirtualDisplay, getState).WillRepeatedly(ReturnRef(mOutputStates[3]));
+ }
+
+ void setOutputs(std::initializer_list<std::shared_ptr<mock::Output>> outputs) {
+ for (auto& output : outputs) {
+ // If we call mEngine.present, prepare and present will be called on all the
+ // outputs in mRefreshArgs, but that's not the interesting part of the test.
+ EXPECT_CALL(*output, prepare(Ref(mRefreshArgs), _)).Times(1);
+ EXPECT_CALL(*output, present(Ref(mRefreshArgs)))
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+
+ mRefreshArgs.outputs.push_back(std::move(output));
+ }
+ }
+};
+
+TEST_F(CompositionEngineOffloadTest, basic) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true));
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mDisplay2});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, dependsOnSupport) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0);
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mDisplay2});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, dependsOnSupport2) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(false));
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mDisplay2});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, dependsOnFlag) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0);
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0);
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
+ setOutputs({mDisplay1, mDisplay2});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, oneDisplay) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0);
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, virtualDisplay) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0);
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mDisplay2, mVirtualDisplay});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, virtualDisplay2) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0);
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mVirtualDisplay});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, halVirtual) {
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true));
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
+ EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mHalVirtualDisplay});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, ordering) {
+ EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0);
+ EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true));
+
+ EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(1);
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, dependsOnEnabled) {
+ // Disable mDisplay2.
+ mOutputStates[1].isEnabled = false;
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+
+ // This is not actually called, because it is not enabled, but this distinguishes
+ // from the case where it did not return true.
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mDisplay2});
+
+ mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineOffloadTest, disabledDisplaysDoNotPreventOthersFromOffloading) {
+ // Disable mDisplay2.
+ mOutputStates[1].isEnabled = false;
+ EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true));
+
+ // This is not actually called, because it is not enabled, but this distinguishes
+ // from the case where it did not return true.
+ EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillRepeatedly(Return(true));
+ EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true));
+
+ EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
+ EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
+ EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0);
+
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ setOutputs({mDisplay1, mDisplay2, mHalVirtualDisplay});
+
+ mEngine.present(mRefreshArgs);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 027004a..a95a5c6 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);
@@ -936,7 +936,7 @@
mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect no calls to queueBuffer if composition was skipped.
- EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+ EXPECT_CALL(*renderSurface, queueBuffer(_, _)).Times(1);
// Expect a call to signal no expensive rendering since there is no client composition.
EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false));
@@ -957,7 +957,7 @@
gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect no calls to queueBuffer if composition was skipped.
- EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+ EXPECT_CALL(*renderSurface, queueBuffer(_, _)).Times(0);
EXPECT_CALL(*renderSurface, beginFrame(false));
gpuDisplay->editState().isEnabled = true;
@@ -978,7 +978,7 @@
gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect no calls to queueBuffer if composition was skipped.
- EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+ EXPECT_CALL(*renderSurface, queueBuffer(_, _)).Times(0);
EXPECT_CALL(*renderSurface, beginFrame(false));
gpuDisplay->editState().isEnabled = true;
@@ -999,7 +999,7 @@
gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
// We expect a single call to queueBuffer when composition is not skipped.
- EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+ EXPECT_CALL(*renderSurface, queueBuffer(_, _)).Times(1);
EXPECT_CALL(*renderSurface, beginFrame(true));
gpuDisplay->editState().isEnabled = true;
@@ -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..9e35717 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -54,12 +54,13 @@
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_METHOD5(setClientTarget,
- status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
- ui::Dataspace));
+ MOCK_METHOD(status_t, getDeviceCompositionChanges,
+ (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
+ Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
+ MOCK_METHOD(status_t, setClientTarget,
+ (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
+ float),
+ (override));
MOCK_METHOD2(presentAndGetReleaseFences,
status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
@@ -147,8 +148,7 @@
MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
- MOCK_METHOD(status_t, notifyExpectedPresentIfRequired,
- (PhysicalDisplayId, nsecs_t, int32_t, int32_t));
+ MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index f74ef4c..7253354 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,11 +38,10 @@
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
- MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(void, reportActualWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
- MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
MOCK_METHOD(void, setGpuFenceTime,
(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
MOCK_METHOD(void, setHwcValidateTiming,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cdcb68d..30ba9e4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -15,6 +15,7 @@
*/
#include <android-base/stringprintf.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -35,7 +36,10 @@
#include <cmath>
#include <cstdint>
+#include <variant>
+#include <common/FlagManager.h>
+#include <common/test/FlagUtils.h>
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
#include "RegionMatcher.h"
@@ -43,6 +47,8 @@
namespace android::compositionengine {
namespace {
+using namespace com::android::graphics::surfaceflinger;
+
using testing::_;
using testing::ByMove;
using testing::ByRef;
@@ -54,6 +60,7 @@
using testing::Invoke;
using testing::IsEmpty;
using testing::Mock;
+using testing::NiceMock;
using testing::Pointee;
using testing::Property;
using testing::Ref;
@@ -2007,7 +2014,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 +2036,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 +2056,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 +2902,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 +2939,7 @@
mOutput.mState.isEnabled = false;
InSequence seq;
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2944,7 +2951,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);
@@ -2959,8 +2966,8 @@
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
- EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2978,7 +2985,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>*));
@@ -2988,11 +2995,15 @@
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
+ EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
}
StrictMock<OutputPartialMock> mOutput;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
};
TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
@@ -3020,7 +3031,34 @@
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
- EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
+
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(std::move(result));
+}
+
+TEST_F(OutputFinishFrameTest, queuesBufferWithHdrSdrRatio) {
+ SET_FLAG_FOR_TEST(flags::fp16_client_target, true);
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ auto texture = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_FP16,
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_SW_READ_OFTEN),
+ mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+ mOutput.mState.displayBrightnessNits = 400.f;
+ mOutput.mState.sdrWhitePointNits = 200.f;
+ mOutput.mState.dataspace = ui::Dataspace::V0_SCRGB;
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(texture), Return(true)));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
+ .WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_, 2.f));
impl::GpuCompositionResult result;
mOutput.finishFrame(std::move(result));
@@ -3030,7 +3068,7 @@
mOutput.mState.isEnabled = true;
mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::SUCCESS;
InSequence seq;
- EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
impl::GpuCompositionResult result;
mOutput.finishFrame(std::move(result));
@@ -3052,19 +3090,19 @@
composeSurfaces(RegionEq(Region::INVALID_REGION), result.buffer,
Eq(ByRef(result.fence))))
.WillOnce(Return(ByMove(base::unique_fd())));
- EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
mOutput.finishFrame(std::move(result));
}
/*
- * 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 +3142,7 @@
TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
@@ -3119,10 +3157,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 +3179,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 +3202,7 @@
EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
});
- mOutput.postFramebuffer();
+ mOutput.presentFrameAndReleaseLayers();
}
TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
@@ -3177,7 +3215,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 +3225,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 +3250,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 +3270,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());
@@ -3322,6 +3360,7 @@
static constexpr float kDefaultAvgLuminance = 0.7f;
static constexpr float kDefaultMinLuminance = 0.1f;
static constexpr float kDisplayLuminance = 400.f;
+ static constexpr float kWhitePointLuminance = 300.f;
static constexpr float kClientTargetLuminanceNits = 200.f;
static constexpr float kClientTargetBrightness = 0.5f;
@@ -3632,7 +3671,7 @@
OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
@@ -3659,6 +3698,14 @@
: public CallOrderStateMachineHelper<TestType, OutputWithDisplayBrightnessNits> {
auto withDisplayBrightnessNits(float nits) {
getInstance()->mOutput.mState.displayBrightnessNits = nits;
+ return nextState<OutputWithSdrWhitePointNits>();
+ }
+ };
+
+ struct OutputWithSdrWhitePointNits
+ : public CallOrderStateMachineHelper<TestType, OutputWithSdrWhitePointNits> {
+ auto withSdrWhitePointNits(float nits) {
+ getInstance()->mOutput.mState.sdrWhitePointNits = nits;
return nextState<OutputWithDimmingStage>();
}
};
@@ -3688,6 +3735,35 @@
// May be called zero or one times.
EXPECT_CALL(getInstance()->mOutput, getSkipColorTransform())
.WillRepeatedly(Return(skip));
+ return nextState<PixelFormatState>();
+ }
+ };
+
+ struct PixelFormatState : public CallOrderStateMachineHelper<TestType, PixelFormatState> {
+ auto withPixelFormat(std::optional<PixelFormat> format) {
+ // May be called zero or one times.
+ if (format) {
+ auto outputBuffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::
+ make(1u, 1u, *format,
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_SW_READ_OFTEN),
+ getInstance()->mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::
+ READABLE |
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ EXPECT_CALL(*getInstance()->mRenderSurface, dequeueBuffer(_))
+ .WillRepeatedly(Return(outputBuffer));
+ }
+ return nextState<DataspaceState>();
+ }
+ };
+
+ struct DataspaceState : public CallOrderStateMachineHelper<TestType, DataspaceState> {
+ auto withDataspace(ui::Dataspace dataspace) {
+ getInstance()->mOutput.mState.dataspace = dataspace;
return nextState<ExpectDisplaySettingsState>();
}
};
@@ -3709,10 +3785,13 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3736,10 +3815,13 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3763,11 +3845,14 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(
aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3791,9 +3876,12 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(true)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(aidl::android::hardware::graphics::composer3::RenderIntent::ENHANCE)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3816,10 +3904,13 @@
verify().ifMixedCompositionIs(true)
.andIfUsesHdr(false)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3842,10 +3933,13 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3868,10 +3962,13 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(false)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3895,10 +3992,13 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
.withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
.withRenderIntent(
aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
.andIfSkipColorTransform(true)
+ .withPixelFormat(std::nullopt)
+ .withDataspace(kDefaultOutputDataspace)
.thenExpectDisplaySettingsUsed(
{.physicalDisplay = kDefaultOutputDestinationClip,
.clip = kDefaultOutputViewport,
@@ -3917,6 +4017,38 @@
.expectAFenceWasReturned();
}
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+ usesExpectedDisplaySettingsWithFp16Buffer) {
+ SET_FLAG_FOR_TEST(flags::fp16_client_target, true);
+ ALOGE("alecmouri: %d", flags::fp16_client_target());
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(true)
+ .withDisplayBrightnessNits(kDisplayLuminance)
+ .withSdrWhitePointNits(kWhitePointLuminance)
+ .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
+ .withRenderIntent(
+ aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
+ .andIfSkipColorTransform(true)
+ .withPixelFormat(PIXEL_FORMAT_RGBA_FP16)
+ .withDataspace(ui::Dataspace::V0_SCRGB)
+ .thenExpectDisplaySettingsUsed(
+ {.physicalDisplay = kDefaultOutputDestinationClip,
+ .clip = kDefaultOutputViewport,
+ .maxLuminance = kDefaultMaxLuminance,
+ .currentLuminanceNits = kDisplayLuminance,
+ .outputDataspace = ui::Dataspace::V0_SCRGB,
+ .colorTransform = kDefaultColorTransformMat,
+ .deviceHandlesColorTransform = true,
+ .orientation = kDefaultOutputOrientationFlags,
+ .targetLuminanceNits = kClientTargetLuminanceNits * 0.75f,
+ .dimmingStage =
+ aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR,
+ .renderIntent = aidl::android::hardware::graphics::composer3::RenderIntent::
+ COLORIMETRIC})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
struct Layer {
Layer() {
@@ -3962,7 +4094,11 @@
};
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = false;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
@@ -3976,7 +4112,11 @@
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
@@ -3998,7 +4138,11 @@
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
@@ -4011,7 +4155,11 @@
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
- mOutput.mState.isSecure = true;
+ if (FlagManager::getInstance().display_protected()) {
+ mOutput.mState.isProtected = true;
+ } else {
+ mOutput.mState.isSecure = true;
+ }
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
@@ -4090,6 +4238,7 @@
GenerateClientCompositionRequestsTest() {
mOutput.mState.needsFiltering = false;
+ mOutput.mState.isProtected = true;
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
@@ -4114,6 +4263,7 @@
mOutput.mState.displaySpace.setOrientation(kDisplayOrientation);
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = false;
+ mOutput.mState.isProtected = true;
for (size_t i = 0; i < mLayers.size(); i++) {
mLayers[i].mOutputLayerState.clearClientTarget = false;
@@ -4576,7 +4726,7 @@
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
@@ -4589,7 +4739,7 @@
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
@@ -4602,7 +4752,7 @@
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
@@ -4780,7 +4930,7 @@
Region(Rect(0, 0, 1000, 1000)),
false, /* needs filtering */
true, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kPortraitViewport,
kOutputDataspace,
true /* realContentIsVisible */,
@@ -4799,7 +4949,7 @@
Region(Rect(1000, 0, 2000, 1000)),
false, /* needs filtering */
true, /* secure */
- true, /* supports protected content */
+ true, /* isProtected */
kPortraitViewport,
kOutputDataspace,
true /* realContentIsVisible */,
@@ -4900,5 +5050,54 @@
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
}
+struct OutputPresentFrameAndReleaseLayersAsyncTest : public ::testing::Test {
+ // Piggy-back on OutputPrepareFrameAsyncTest's version to avoid some boilerplate.
+ struct OutputPartialMock : public OutputPrepareFrameAsyncTest::OutputPartialMock {
+ // Set up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD0(presentFrameAndReleaseLayersAsync, ftl::Future<std::monostate>());
+ };
+ OutputPresentFrameAndReleaseLayersAsyncTest() {
+ mOutput->setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ mOutput->setCompositionEnabled(true);
+ mRefreshArgs.outputs = {mOutput};
+ }
+
+ mock::DisplayColorProfile* mDisplayColorProfile = new NiceMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new NiceMock<mock::RenderSurface>();
+ std::shared_ptr<OutputPartialMock> mOutput{std::make_shared<NiceMock<OutputPartialMock>>()};
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, notCalledWhenNotRequested) {
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()).Times(0);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+
+ mOutput->present(mRefreshArgs);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledWhenRequested) {
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(0);
+
+ mOutput->offloadPresentNextFrame();
+ mOutput->present(mRefreshArgs);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledForOneFrame) {
+ ::testing::InSequence inseq;
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ .WillOnce(Return(ftl::yield<std::monostate>({})));
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+
+ mOutput->offloadPresentNextFrame();
+ mOutput->present(mRefreshArgs);
+ mOutput->present(mRefreshArgs);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 83937a6..edfaa26 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -263,9 +263,9 @@
state.flipClientTarget = false;
EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
- EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+ EXPECT_CALL(*mDisplaySurface, advanceFrame(0.5f)).Times(1);
- mSurface.queueBuffer(base::unique_fd());
+ mSurface.queueBuffer(base::unique_fd(), 0.5f);
EXPECT_EQ(buffer.get(), mSurface.mutableTextureForTest().get());
}
@@ -283,9 +283,9 @@
EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+ EXPECT_CALL(*mDisplaySurface, advanceFrame(0.5f)).Times(1);
- mSurface.queueBuffer(base::unique_fd());
+ mSurface.queueBuffer(base::unique_fd(), 0.5f);
EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
@@ -303,9 +303,9 @@
EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+ EXPECT_CALL(*mDisplaySurface, advanceFrame(0.5f)).Times(1);
- mSurface.queueBuffer(base::unique_fd());
+ mSurface.queueBuffer(base::unique_fd(), 0.5f);
EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
@@ -323,9 +323,9 @@
DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+ EXPECT_CALL(*mDisplaySurface, advanceFrame(0.5f)).Times(1);
- mSurface.queueBuffer(base::unique_fd());
+ mSurface.queueBuffer(base::unique_fd(), 0.5f);
EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
@@ -345,9 +345,9 @@
EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getBuffer()->getNativeBuffer(), -1))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+ EXPECT_CALL(*mDisplaySurface, advanceFrame(0.5f)).Times(1);
- mSurface.queueBuffer(base::unique_fd());
+ mSurface.queueBuffer(base::unique_fd(), 0.5f);
EXPECT_EQ(nullptr, mSurface.mutableTextureForTest().get());
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 00590e6..d2eff75 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <common/include/common/test/FlagUtils.h>
+#include "com_android_graphics_surfaceflinger_flags.h"
+
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/Flattener.h>
@@ -223,9 +226,27 @@
}
TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+
mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
+ expectAllLayersFlattened(layers);
+}
+
+TEST_F(FlattenerTest, unflattenLayers_onlySourceCropMoved) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ true);
+
auto& layerState1 = mTestLayers[0]->layerState;
auto& layerState2 = mTestLayers[1]->layerState;
@@ -235,7 +256,14 @@
};
initializeFlattener(layers);
- expectAllLayersFlattened(layers);
+
+ mTestLayers[0]->outputLayerCompositionState.sourceCrop = FloatRect{0.f, 0.f, 100.f, 100.f};
+ mTestLayers[1]->outputLayerCompositionState.sourceCrop = FloatRect{8.f, 16.f, 108.f, 116.f};
+
+ // only source crop is moved, so no flatten
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
}
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5b6591a..799d62c 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -63,14 +63,16 @@
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())),
+ mPendingModeFpsTrace(concatId("PendingModeFps")),
+ mActiveModeFpsTrace(concatId("ActiveModeFps")),
+ mRenderRateFpsTrace(concatId("RenderRateFps")),
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary),
mRequestedRefreshRate(args.requestedRefreshRate),
- mRefreshRateSelector(std::move(args.refreshRateSelector)) {
+ mRefreshRateSelector(std::move(args.refreshRateSelector)),
+ mHasDesiredModeTrace(concatId("HasDesiredMode"), false) {
mCompositionDisplay->editState().isSecure = args.isSecure;
+ mCompositionDisplay->editState().isProtected = args.isProtected;
mCompositionDisplay->createRenderSurface(
compositionengine::RenderSurfaceCreationArgsBuilder()
.setDisplayWidth(ANativeWindow_getWidth(args.nativeWindow.get()))
@@ -209,31 +211,28 @@
}
void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
- ATRACE_INT(mActiveModeFPSTrace.c_str(), vsyncRate.getIntValue());
- ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
+ ATRACE_INT(mActiveModeFpsTrace.c_str(), vsyncRate.getIntValue());
+ ATRACE_INT(mRenderRateFpsTrace.c_str(), renderFps.getIntValue());
mRefreshRateSelector->setActiveMode(modeId, renderFps);
updateRefreshRateOverlayRate(vsyncRate, renderFps);
}
-status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline* outTimeline) {
- if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) {
- ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
- info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str()
- : "null",
- to_string(getId()).c_str());
- return BAD_VALUE;
- }
- mUpcomingActiveMode = info;
+bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline& outTimeline) {
+ mPendingModeOpt = std::move(desiredMode);
mIsModeSetPending = true;
- const auto& pendingMode = *info.modeOpt->modePtr;
- ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), pendingMode.getVsyncRate().getIntValue());
+ const auto& mode = *mPendingModeOpt->mode.modePtr;
- return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), pendingMode.getHwcId(),
- constraints, outTimeline);
+ if (mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode.getHwcId(), constraints,
+ &outTimeline) != OK) {
+ return false;
+ }
+
+ ATRACE_INT(mPendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+ return true;
}
void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
@@ -354,6 +353,10 @@
return mCompositionDisplay->isSecure();
}
+void DisplayDevice::setSecure(bool secure) {
+ mCompositionDisplay->setSecure(secure);
+}
+
const Rect DisplayDevice::getBounds() const {
return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
}
@@ -523,59 +526,59 @@
}
}
-auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
- -> DesiredActiveModeAction {
+auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force)
+ -> DesiredModeAction {
ATRACE_CALL();
- LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
- LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(),
+ const auto& desiredModePtr = desiredMode.mode.modePtr;
+
+ LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(),
"DisplayId mismatch");
- ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str());
+ ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str());
- std::scoped_lock lock(mActiveModeLock);
- if (mDesiredActiveModeChanged) {
- // If a mode change is pending, just cache the latest request in mDesiredActiveMode
- const auto prevConfig = mDesiredActiveMode.event;
- mDesiredActiveMode = info;
- mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
- return DesiredActiveModeAction::None;
+ std::scoped_lock lock(mDesiredModeLock);
+ if (mDesiredModeOpt) {
+ // A mode transition was already scheduled, so just override the desired mode.
+ const bool emitEvent = mDesiredModeOpt->emitEvent;
+ mDesiredModeOpt = std::move(desiredMode);
+ mDesiredModeOpt->emitEvent |= emitEvent;
+ return DesiredModeAction::None;
}
- const auto& desiredMode = *info.modeOpt->modePtr;
-
- // Check if we are already at the desired mode
- const auto currentMode = refreshRateSelector().getActiveMode();
- if (!force && currentMode.modePtr->getId() == desiredMode.getId()) {
- if (currentMode == info.modeOpt) {
- return DesiredActiveModeAction::None;
+ // If the desired mode is already active...
+ const auto activeMode = refreshRateSelector().getActiveMode();
+ if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
+ if (activeMode == desiredMode.mode) {
+ return DesiredModeAction::None;
}
- setActiveMode(desiredMode.getId(), desiredMode.getVsyncRate(), info.modeOpt->fps);
- return DesiredActiveModeAction::InitiateRenderRateSwitch;
+ // ...but the render rate changed:
+ setActiveMode(desiredModePtr->getId(), desiredModePtr->getVsyncRate(),
+ desiredMode.mode.fps);
+ return DesiredModeAction::InitiateRenderRateSwitch;
}
- // Set the render frame rate to the current physical refresh rate to schedule the next
+ // Set the render frame rate to the active physical refresh rate to schedule the next
// frame as soon as possible.
- setActiveMode(currentMode.modePtr->getId(), currentMode.modePtr->getVsyncRate(),
- currentMode.modePtr->getVsyncRate());
+ setActiveMode(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
+ activeMode.modePtr->getVsyncRate());
// Initiate a mode change.
- mDesiredActiveModeChanged = true;
- mDesiredActiveMode = info;
- return DesiredActiveModeAction::InitiateDisplayModeSwitch;
+ mDesiredModeOpt = std::move(desiredMode);
+ mHasDesiredModeTrace = true;
+ return DesiredModeAction::InitiateDisplayModeSwitch;
}
-std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const {
- std::scoped_lock lock(mActiveModeLock);
- if (mDesiredActiveModeChanged) return mDesiredActiveMode;
- return std::nullopt;
+auto DisplayDevice::getDesiredMode() const -> DisplayModeRequestOpt {
+ std::scoped_lock lock(mDesiredModeLock);
+ return mDesiredModeOpt;
}
-void DisplayDevice::clearDesiredActiveModeState() {
- std::scoped_lock lock(mActiveModeLock);
- mDesiredActiveMode.event = scheduler::DisplayModeEvent::None;
- mDesiredActiveModeChanged = false;
+void DisplayDevice::clearDesiredMode() {
+ std::scoped_lock lock(mDesiredModeLock);
+ mDesiredModeOpt.reset();
+ mHasDesiredModeTrace = false;
}
void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index a044534..97b56a2 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -17,7 +17,6 @@
#pragma once
#include <memory>
-#include <optional>
#include <string>
#include <unordered_map>
@@ -25,6 +24,7 @@
#include <android/native_window.h>
#include <binder/IBinder.h>
#include <ftl/concat.h>
+#include <ftl/optional.h>
#include <gui/LayerState.h>
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
@@ -51,6 +51,7 @@
#include "ThreadContext.h"
#include "TracedOrdinal.h"
#include "Utils/Dumper.h"
+
namespace android {
class Fence;
@@ -94,6 +95,7 @@
// isSecure indicates whether this display can be trusted to display
// secure surfaces.
bool isSecure() const;
+ void setSecure(bool secure);
int getWidth() const;
int getHeight() const;
@@ -185,39 +187,19 @@
* Display mode management.
*/
- // TODO(b/241285876): Replace ActiveModeInfo and DisplayModeEvent with DisplayModeRequest.
- struct ActiveModeInfo {
- using Event = scheduler::DisplayModeEvent;
+ enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
- ActiveModeInfo() = default;
- ActiveModeInfo(scheduler::FrameRateMode mode, Event event)
- : modeOpt(std::move(mode)), event(event) {}
+ DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false)
+ EXCLUDES(mDesiredModeLock);
- explicit ActiveModeInfo(display::DisplayModeRequest&& request)
- : ActiveModeInfo(std::move(request.mode),
- request.emitEvent ? Event::Changed : Event::None) {}
+ using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>;
- ftl::Optional<scheduler::FrameRateMode> modeOpt;
- Event event = Event::None;
+ DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock);
+ void clearDesiredMode() EXCLUDES(mDesiredModeLock);
- bool operator!=(const ActiveModeInfo& other) const {
- return modeOpt != other.modeOpt || event != other.event;
- }
- };
-
- enum class DesiredActiveModeAction {
- None,
- InitiateDisplayModeSwitch,
- InitiateRenderRateSwitch
- };
- DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&, bool force = false)
- EXCLUDES(mActiveModeLock);
- std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
- void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
- ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
- return mUpcomingActiveMode;
+ DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) {
+ return mPendingModeOpt;
}
-
bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
@@ -226,9 +208,8 @@
void setActiveMode(DisplayModeId, Fps vsyncRate, Fps renderFps);
- status_t initiateModeChange(const ActiveModeInfo&,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline* outTimeline)
+ bool initiateModeChange(display::DisplayModeRequest&&, const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline& outTimeline)
REQUIRES(kMainThreadContext);
void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps)
@@ -269,6 +250,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;
@@ -277,9 +263,9 @@
const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
std::string mDisplayName;
- std::string mActiveModeFPSTrace;
- std::string mActiveModeFPSHwcTrace;
- std::string mRenderFrameRateFPSTrace;
+ std::string mPendingModeFpsTrace;
+ std::string mActiveModeFpsTrace;
+ std::string mRenderRateFpsTrace;
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
@@ -314,12 +300,11 @@
// This parameter is only used for hdr/sdr ratio overlay
float mHdrSdrRatio = 1.0f;
- mutable std::mutex mActiveModeLock;
- ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
- TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) =
- {ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false};
+ mutable std::mutex mDesiredModeLock;
+ DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock);
+ TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock);
- ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
+ DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext);
bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
@@ -348,6 +333,7 @@
uint32_t height = 0;
std::string displayName;
bool isSecure = false;
+ bool isProtected = false;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
Fps requestedRefreshRate;
@@ -369,6 +355,7 @@
int32_t sequenceId{0};
bool isSecure{false};
+ bool isProtected{false};
sp<ANativeWindow> nativeWindow;
sp<compositionengine::DisplaySurface> displaySurface;
ui::Rotation physicalOrientation{ui::ROTATION_0};
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 1643ad0..64a8ae7 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -24,6 +24,7 @@
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
+#include <common/FlagManager.h>
#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -77,6 +78,7 @@
using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform;
using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+using AidlDisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using AidlFRect = aidl::android::hardware::graphics::common::FRect;
using AidlRect = aidl::android::hardware::graphics::common::Rect;
using AidlTransform = aidl::android::hardware::graphics::common::Transform;
@@ -173,9 +175,9 @@
AidlIComposerCallbackWrapper(HWC2::ComposerCallback& callback) : mCallback(callback) {}
::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override {
- const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED
- : V2_4::IComposerCallback::Connection::DISCONNECTED;
- mCallback.onComposerHalHotplug(translate<Display>(in_display), connection);
+ const auto event = in_connected ? AidlDisplayHotplugEvent::CONNECTED
+ : AidlDisplayHotplugEvent::DISCONNECTED;
+ mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event);
return ::ndk::ScopedAStatus::ok();
}
@@ -215,6 +217,12 @@
return ::ndk::ScopedAStatus::ok();
}
+ ::ndk::ScopedAStatus onHotplugEvent(int64_t in_display,
+ AidlDisplayHotplugEvent event) override {
+ mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event);
+ return ::ndk::ScopedAStatus::ok();
+ }
+
private:
HWC2::ComposerCallback& mCallback;
};
@@ -263,7 +271,10 @@
}
}
}
-
+ if (getLayerLifecycleBatchCommand()) {
+ mEnableLayerCommandBatchingFlag =
+ FlagManager::getInstance().enable_layer_command_batching();
+ }
ALOGI("Loaded AIDL composer3 HAL service");
}
@@ -280,8 +291,8 @@
}
}
-bool AidlComposer::getDisplayConfigurationsSupported() const {
- return mComposerInterfaceVersion >= 3;
+bool AidlComposer::isVrrSupported() const {
+ return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config();
}
std::vector<Capability> AidlComposer::getCapabilities() {
@@ -399,25 +410,58 @@
Error AidlComposer::createLayer(Display display, Layer* outLayer) {
int64_t layer;
- const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
- kMaxLayerBufferCount, &layer);
- if (!status.isOk()) {
- ALOGE("createLayer failed %s", status.getDescription().c_str());
- return static_cast<Error>(status.getServiceSpecificError());
+ Error error = Error::NONE;
+ if (!mEnableLayerCommandBatchingFlag) {
+ const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
+ kMaxLayerBufferCount, &layer);
+ if (!status.isOk()) {
+ ALOGE("createLayer failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ } else {
+ // generate a unique layerID. map in AidlComposer with <SF_layerID, HWC_layerID>
+ // Add this as a new displayCommand in execute command.
+ // return the SF generated layerID instead of calling HWC
+ layer = mLayerID++;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerLifecycleBatchCommandType(translate<int64_t>(display),
+ translate<int64_t>(layer),
+ LayerLifecycleBatchCommandType::CREATE);
+ writer->get().setNewBufferSlotCount(translate<int64_t>(display),
+ translate<int64_t>(layer), kMaxLayerBufferCount);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
}
-
*outLayer = translate<Layer>(layer);
- return Error::NONE;
+ return error;
}
Error AidlComposer::destroyLayer(Display display, Layer layer) {
- const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
- translate<int64_t>(layer));
- if (!status.isOk()) {
- ALOGE("destroyLayer failed %s", status.getDescription().c_str());
- return static_cast<Error>(status.getServiceSpecificError());
+ Error error = Error::NONE;
+ if (!mEnableLayerCommandBatchingFlag) {
+ const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
+ translate<int64_t>(layer));
+ if (!status.isOk()) {
+ ALOGE("destroyLayer failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ } else {
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get()
+ .setLayerLifecycleBatchCommandType(translate<int64_t>(display),
+ translate<int64_t>(layer),
+ LayerLifecycleBatchCommandType::DESTROY);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
}
- return Error::NONE;
+
+ return error;
}
Error AidlComposer::getActiveConfig(Display display, Config* outConfig) {
@@ -583,6 +627,13 @@
return Error::NONE;
}
+bool AidlComposer::getLayerLifecycleBatchCommand() {
+ std::vector<Capability> capabilities = getCapabilities();
+ bool hasCapability = std::find(capabilities.begin(), capabilities.end(),
+ Capability::LAYER_LIFECYCLE_BATCH_COMMAND) != capabilities.end();
+ return hasCapability;
+}
+
Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) {
const auto status = mAidlComposerClient->getOverlaySupport(outProperties);
if (!status.isOk()) {
@@ -658,7 +709,8 @@
Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage) {
+ const std::vector<IComposerClient::Rect>& damage,
+ float hdrSdrRatio) {
const native_handle_t* handle = nullptr;
if (target.get()) {
handle = target->getNativeBuffer()->handle;
@@ -671,7 +723,7 @@
.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
translate<aidl::android::hardware::graphics::common::Dataspace>(
dataspace),
- translate<AidlRect>(damage));
+ translate<AidlRect>(damage), hdrSdrRatio);
} else {
error = Error::BAD_DISPLAY;
}
@@ -749,7 +801,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 +811,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 +830,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 +842,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;
@@ -1570,8 +1626,7 @@
}
bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
-#if 0
- // TODO (b/259132483): Reenable
+ if (!FlagManager::getInstance().multithreaded_present()) return false;
const auto displayId = translate<int64_t>(display);
std::vector<AidlDisplayCapability> capabilities;
const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
@@ -1581,10 +1636,6 @@
}
return std::find(capabilities.begin(), capabilities.end(),
AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end();
-#else
- (void) display;
- return false;
-#endif
}
void AidlComposer::addReader(Display display) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 7693a80..ea0e53a 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -66,7 +66,7 @@
~AidlComposer() override;
bool isSupported(OptionalFeature) const;
- bool getDisplayConfigurationsSupported() const;
+ bool isVrrSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -124,7 +124,8 @@
*/
Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage) override;
+ const std::vector<IComposerClient::Rect>& damage,
+ float hdrSdrRatio) override;
Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
Error setColorTransform(Display display, const float* matrix) override;
Error setOutputBuffer(Display display, const native_handle_t* buffer,
@@ -134,12 +135,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 */
@@ -260,7 +262,7 @@
void removeDisplay(Display) EXCLUDES(mMutex);
void addReader(Display) REQUIRES(mMutex);
void removeReader(Display) REQUIRES(mMutex);
-
+ bool getLayerLifecycleBatchCommand();
bool hasMultiThreadedPresentSupport(Display);
// 64KiB minus a small space for metadata such as read/write pointers
@@ -291,6 +293,8 @@
ftl::SharedMutex mMutex;
int32_t mComposerInterfaceVersion = 1;
+ bool mEnableLayerCommandBatchingFlag = false;
+ std::atomic<int64_t> mLayerID = 1;
// Buffer slots for layers are cleared by setting the slot buffer to this buffer.
sp<GraphicBuffer> mClearSlotBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 6704d88..bc067a0 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -105,7 +105,7 @@
};
virtual bool isSupported(OptionalFeature) const = 0;
- virtual bool getDisplayConfigurationsSupported() const = 0;
+ virtual bool isVrrSupported() const = 0;
virtual std::vector<aidl::android::hardware::graphics::composer3::Capability>
getCapabilities() = 0;
@@ -163,7 +163,8 @@
*/
virtual Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage) = 0;
+ const std::vector<IComposerClient::Rect>& damage,
+ float hdrSdrRatio) = 0;
virtual Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) = 0;
virtual Error setColorTransform(Display display, const float* matrix) = 0;
virtual Error setOutputBuffer(Display display, const native_handle_t* buffer,
@@ -174,11 +175,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..ba0825c 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -29,9 +29,9 @@
#include <scheduler/Fps.h>
+#include <common/FlagManager.h>
#include "DisplayHardware/Hal.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/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index ce602a8..c77cdd4 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -91,7 +91,7 @@
return NO_ERROR;
}
-status_t FramebufferSurface::advanceFrame() {
+status_t FramebufferSurface::advanceFrame(float hdrSdrRatio) {
Mutex::Autolock lock(mMutex);
BufferItem item;
@@ -131,7 +131,7 @@
hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot
}
status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer,
- mDataspace);
+ mDataspace, hdrSdrRatio);
if (result != NO_ERROR) {
ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result);
return result;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 0b863da..2728cf6 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -46,7 +46,7 @@
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
- virtual status_t advanceFrame();
+ virtual status_t advanceFrame(float hdrSdrRatio);
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 0c2b77d..704ece5 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -165,8 +165,7 @@
auto intError = mComposer.getChangedCompositionTypes(
mId, &layerIds, &types);
uint32_t numElements = layerIds.size();
- auto error = static_cast<Error>(intError);
- error = static_cast<Error>(intError);
+ const auto error = static_cast<Error>(intError);
if (error != Error::NONE) {
return error;
}
@@ -446,12 +445,13 @@
}
Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target,
- const sp<Fence>& acquireFence, Dataspace dataspace)
-{
+ const sp<Fence>& acquireFence, Dataspace dataspace,
+ float hdrSdrRatio) {
// TODO: Properly encode client target surface damage
int32_t fenceFd = acquireFence->dup();
- auto intError = mComposer.setClientTarget(mId, slot, target,
- fenceFd, dataspace, std::vector<Hwc2::IComposerClient::Rect>());
+ auto intError =
+ mComposer.setClientTarget(mId, slot, target, fenceFd, dataspace,
+ std::vector<Hwc2::IComposerClient::Rect>(), hdrSdrRatio);
return static_cast<Error>(intError);
}
@@ -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..f907061 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,6 +38,7 @@
#include "Hal.h"
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <aidl/android/hardware/graphics/composer3/Capability.h>
#include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
#include <aidl/android/hardware/graphics/composer3/Color.h>
@@ -64,15 +65,16 @@
namespace hal = android::hardware::graphics::composer::hal;
+using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
// Implement this interface to receive hardware composer events.
//
// These callback functions will generally be called on a hwbinder thread, but
-// when first registering the callback the onComposerHalHotplug() function will
-// immediately be called on the thread calling registerCallback().
+// when first registering the callback the onComposerHalHotplugEvent() function
+// will immediately be called on the thread calling registerCallback().
struct ComposerCallback {
- virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
+ virtual void onComposerHalHotplugEvent(hal::HWDisplayId, DisplayHotplugEvent) = 0;
virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
std::optional<hal::VsyncPeriodNanos>) = 0;
@@ -139,7 +141,8 @@
[[nodiscard]] virtual hal::Error present(android::sp<android::Fence>* outPresentFence) = 0;
[[nodiscard]] virtual hal::Error setClientTarget(
uint32_t slot, const android::sp<android::GraphicBuffer>& target,
- const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace) = 0;
+ const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace,
+ float hdrSdrRatio) = 0;
[[nodiscard]] virtual hal::Error setColorMode(hal::ColorMode mode,
hal::RenderIntent renderIntent) = 0;
[[nodiscard]] virtual hal::Error setColorTransform(const android::mat4& matrix) = 0;
@@ -148,9 +151,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,
@@ -226,17 +230,17 @@
hal::Error present(android::sp<android::Fence>* outPresentFence) override;
hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence,
- hal::Dataspace dataspace) override;
+ hal::Dataspace dataspace, float hdrSdrRatio) override;
hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
hal::Error setColorTransform(const android::mat4& matrix) override;
hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
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..cf1c3c1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -15,6 +15,7 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <chrono>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -76,6 +77,8 @@
using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::VrrConfig;
+using namespace std::string_literals;
namespace hal = android::hardware::graphics::composer::hal;
namespace android {
@@ -88,7 +91,8 @@
: mComposer(std::move(composer)),
mMaxVirtualDisplayDimension(static_cast<size_t>(sysprop::max_virtual_display_dimension(0))),
mUpdateDeviceProductInfoOnHotplugReconnect(
- sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
+ sysprop::update_device_product_info_on_hotplug_reconnect(false)),
+ mEnableVrrTimeout(base::GetBoolProperty("debug.sf.vrr_timeout_hint_enabled"s, false)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
: HWComposer(Hwc2::Composer::create(composerServiceName)) {}
@@ -268,7 +272,7 @@
const auto hwcDisplayId = mDisplayData.at(displayId).hwcDisplay->getId();
- if (mComposer->getDisplayConfigurationsSupported()) {
+ if (mComposer->isVrrSupported()) {
return getModesFromDisplayConfigurations(hwcDisplayId, maxFrameIntervalNs);
}
@@ -298,6 +302,10 @@
hwcMode.dpiY = config.dpi->y;
}
+ if (!mEnableVrrTimeout) {
+ hwcMode.vrrConfig->notifyExpectedPresentConfig = {};
+ }
+
modes.push_back(hwcMode);
}
@@ -432,12 +440,12 @@
status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
- ui::Dataspace dataspace) {
+ ui::Dataspace dataspace, float hdrSdrRatio) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
ALOGV("%s for display %s", __FUNCTION__, to_string(displayId).c_str());
auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
- auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace);
+ auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace, hdrSdrRatio);
RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
return NO_ERROR;
}
@@ -445,7 +453,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 +493,12 @@
}();
displayData.validateWasSkipped = false;
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
+ ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue());
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 +513,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)) {
@@ -878,23 +887,15 @@
return NO_ERROR;
}
-status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
- nsecs_t expectedPresentTime,
- int32_t frameIntervalNs, int32_t timeoutNs) {
+status_t HWComposer::notifyExpectedPresent(PhysicalDisplayId displayId,
+ TimePoint expectedPresentTime, Fps frameInterval) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
-
- auto& displayData = mDisplayData[displayId];
- if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp &&
- expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) {
- return NO_ERROR;
- }
-
- displayData.lastExpectedPresentTimestamp = expectedPresentTime;
- ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__,
- expectedPresentTime, frameIntervalNs);
- const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(),
- expectedPresentTime, frameIntervalNs);
-
+ ATRACE_FORMAT("%s ExpectedPresentTime in %.2fms frameInterval %.2fms", __func__,
+ ticks<std::milli, float>(expectedPresentTime - TimePoint::now()),
+ ticks<std::milli, float>(Duration::fromNs(frameInterval.getPeriodNsecs())));
+ const auto error = mComposer->notifyExpectedPresent(mDisplayData[displayId].hwcDisplay->getId(),
+ expectedPresentTime.ns(),
+ frameInterval.getPeriodNsecs());
if (error != hal::Error::NONE) {
ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str());
return INVALID_OPERATION;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 726a8ea..fb32ff4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -147,10 +147,12 @@
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;
+ const sp<GraphicBuffer>& target, ui::Dataspace,
+ float hdrSdrRatio) = 0;
// Present layers to the display and read releaseFences.
virtual status_t presentAndGetReleaseFences(
@@ -301,9 +303,8 @@
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 notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
+ Fps frameInterval) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -346,11 +347,12 @@
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,
- const sp<GraphicBuffer>& target, ui::Dataspace) override;
+ const sp<GraphicBuffer>& target, ui::Dataspace,
+ float hdrSdrRatio) override;
// Present layers to the display and read releaseFences.
status_t presentAndGetReleaseFences(
@@ -462,8 +464,8 @@
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 notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
+ Fps frameInterval) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -497,8 +499,6 @@
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 0;
- nsecs_t lastExpectedPresentTimestamp = 0;
-
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
bool validateWasSkipped;
@@ -543,6 +543,7 @@
const size_t mMaxVirtualDisplayDimension;
const bool mUpdateDeviceProductInfoOnHotplugReconnect;
+ bool mEnableVrrTimeout;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 20f7548..e3d9622 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
@@ -58,6 +59,7 @@
using ContentType = IComposerClient::ContentType;
using Capability = IComposer::Capability;
using ClientTargetProperty = IComposerClient::ClientTargetProperty;
+using DisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using DisplayRequest = IComposerClient::DisplayRequest;
using DisplayType = IComposerClient::DisplayType;
using HWConfigId = V2_1::Config;
@@ -167,10 +169,8 @@
out << "}, ";
out << "notifyExpectedPresentConfig={";
if (vrrConfig->notifyExpectedPresentConfig) {
- out << "notifyExpectedPresentHeadsUpNs="
- << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentHeadsUpNs
- << ", notifyExpectedPresentTimeoutNs="
- << vrrConfig->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs;
+ out << "headsUpNs=" << vrrConfig->notifyExpectedPresentConfig->headsUpNs
+ << ", timeoutNs=" << vrrConfig->notifyExpectedPresentConfig->timeoutNs;
}
out << "}}";
return out.str();
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c13e568..c4ff9cc 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -25,6 +25,7 @@
#include "HidlComposerHal.h"
#include <SurfaceFlingerProperties.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <android/binder_manager.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
@@ -38,6 +39,7 @@
#include <algorithm>
#include <cinttypes>
+using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using aidl::android::hardware::graphics::common::HdrConversionCapability;
using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using aidl::android::hardware::graphics::composer3::Capability;
@@ -64,8 +66,13 @@
ComposerCallbackBridge(ComposerCallback& callback, bool vsyncSwitchingSupported)
: mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+ // For code sharing purposes, `ComposerCallback` (implemented by SurfaceFlinger)
+ // replaced `onComposerHalHotplug` with `onComposerHalHotplugEvent` by converting
+ // from HIDL's connection into an AIDL DisplayHotplugEvent.
Return<void> onHotplug(Display display, Connection connection) override {
- mCallback.onComposerHalHotplug(display, connection);
+ const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED
+ : DisplayHotplugEvent::DISCONNECTED;
+ mCallback.onComposerHalHotplugEvent(display, event);
return Void();
}
@@ -269,8 +276,8 @@
}
}
-bool HidlComposer::getDisplayConfigurationsSupported() const {
- // getDisplayConfigurations is not supported on the HIDL composer.
+bool HidlComposer::isVrrSupported() const {
+ // VRR is not supported on the HIDL composer.
return false;
};
@@ -601,7 +608,8 @@
Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage) {
+ const std::vector<IComposerClient::Rect>& damage,
+ float /*hdrSdrRatio*/) {
mWriter.selectDisplay(display);
const native_handle_t* handle = nullptr;
@@ -665,7 +673,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 +690,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..d78bfb7 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -167,7 +167,7 @@
~HidlComposer() override;
bool isSupported(OptionalFeature) const;
- bool getDisplayConfigurationsSupported() const;
+ bool isVrrSupported() const;
std::vector<aidl::android::hardware::graphics::composer3::Capability> getCapabilities()
override;
@@ -226,7 +226,8 @@
*/
Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage) override;
+ const std::vector<IComposerClient::Rect>& damage,
+ float hdrSdrRatio) override;
Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
Error setColorTransform(Display display, const float* matrix) override;
Error setOutputBuffer(Display display, const native_handle_t* buffer,
@@ -236,12 +237,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..a0c943b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -31,10 +31,6 @@
#include <utils/Mutex.h>
#include <utils/Trace.h>
-#include <aidl/android/hardware/power/IPower.h>
-#include <aidl/android/hardware/power/IPowerHintSession.h>
-#include <aidl/android/hardware/power/WorkDuration.h>
-
#include <binder/IServiceManager.h>
#include "../SurfaceFlingerProperties.h"
@@ -50,7 +46,6 @@
namespace impl {
using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::Mode;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::WorkDuration;
@@ -144,11 +139,13 @@
if (!mBootFinished.load()) {
return;
}
- if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ if (usePowerHintSession()) {
std::lock_guard lock(mHintSessionMutex);
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
- if (!ret.isOk()) {
- mHintSessionRunning = false;
+ if (ensurePowerHintSessionRunning()) {
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
+ if (!ret.isOk()) {
+ mHintSession = nullptr;
+ }
}
}
}
@@ -162,11 +159,13 @@
if (mSendUpdateImminent.exchange(false)) {
ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
- if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+ if (usePowerHintSession()) {
std::lock_guard lock(mHintSessionMutex);
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
- if (!ret.isOk()) {
- mHintSessionRunning = false;
+ if (ensurePowerHintSessionRunning()) {
+ auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+ if (!ret.isOk()) {
+ mHintSession = nullptr;
+ }
}
}
@@ -193,14 +192,12 @@
}
}
-// checks both if it supports and if it's enabled
bool PowerAdvisor::usePowerHintSession() {
// uses cached value since the underlying support and flag are unlikely to change at runtime
return mHintSessionEnabled.value_or(false) && supportsPowerHintSession();
}
bool PowerAdvisor::supportsPowerHintSession() {
- // cache to avoid needing lock every time
if (!mSupportsHintSession.has_value()) {
mSupportsHintSession = getPowerHal().getHintSessionPreferredRate().isOk();
}
@@ -208,10 +205,15 @@
}
bool PowerAdvisor::ensurePowerHintSessionRunning() {
- if (!mHintSessionRunning && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
- startPowerHintSession(mHintSessionThreadIds);
+ if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
+ auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mHintSessionThreadIds, mTargetDuration.ns());
+
+ if (ret.isOk()) {
+ mHintSession = ret.value();
+ }
}
- return mHintSessionRunning;
+ return mHintSession != nullptr;
}
void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
@@ -223,15 +225,16 @@
{
mTargetDuration = targetDuration;
if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
- if (ensurePowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+ if (targetDuration == mLastTargetDurationSent) return;
+ std::lock_guard lock(mHintSessionMutex);
+ if (ensurePowerHintSessionRunning()) {
ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
mLastTargetDurationSent = targetDuration;
- std::lock_guard lock(mHintSessionMutex);
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
ret.getDescription().c_str());
- mHintSessionRunning = false;
+ mHintSession = nullptr;
}
}
}
@@ -244,16 +247,12 @@
}
ATRACE_CALL();
std::optional<Duration> actualDuration = estimateWorkDuration();
- if (!actualDuration.has_value() || actualDuration < 0ns || !ensurePowerHintSessionRunning()) {
+ if (!actualDuration.has_value() || actualDuration < 0ns) {
ALOGV("Failed to send actual work duration, skipping");
return;
}
actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin);
mActualDuration = actualDuration;
- WorkDuration duration;
- duration.durationNanos = actualDuration->ns();
- duration.timeStampNanos = TimePoint::now().ns();
- mHintSessionQueue.push_back(duration);
if (sTraceHintSessionData) {
ATRACE_INT64("Measured duration", actualDuration->ns());
@@ -269,13 +268,34 @@
actualDuration->ns(), mLastTargetDurationSent.ns(),
Duration{*actualDuration - mLastTargetDurationSent}.ns());
+ if (mTimingTestingMode) {
+ mDelayReportActualMutexAcquisitonPromise.get_future().wait();
+ mDelayReportActualMutexAcquisitonPromise = std::promise<bool>{};
+ }
+
{
std::lock_guard lock(mHintSessionMutex);
+ if (!ensurePowerHintSessionRunning()) {
+ ALOGV("Hint session not running and could not be started, skipping");
+ return;
+ }
+
+ WorkDuration duration{
+ .timeStampNanos = TimePoint::now().ns(),
+ // TODO(b/284324521): Correctly calculate total duration.
+ .durationNanos = actualDuration->ns(),
+ .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+ .cpuDurationNanos = actualDuration->ns(),
+ // TODO(b/284324521): Calculate RenderEngine GPU time.
+ .gpuDurationNanos = 0,
+ };
+ mHintSessionQueue.push_back(duration);
+
auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
if (!ret.isOk()) {
ALOGW("Failed to report actual work durations with error: %s",
ret.getDescription().c_str());
- mHintSessionRunning = false;
+ mHintSession = nullptr;
return;
}
}
@@ -286,7 +306,8 @@
mHintSessionEnabled = enabled;
}
-bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) {
+bool PowerAdvisor::startPowerHintSession(std::vector<int32_t>&& threadIds) {
+ mHintSessionThreadIds = threadIds;
if (!mBootFinished.load()) {
return false;
}
@@ -294,25 +315,14 @@
ALOGI("Cannot start power hint session: disabled or unsupported");
return false;
}
- if (mHintSessionRunning) {
+ LOG_ALWAYS_FATAL_IF(mHintSessionThreadIds.empty(),
+ "No thread IDs provided to power hint session!");
+ std::lock_guard lock(mHintSessionMutex);
+ if (mHintSession != nullptr) {
ALOGE("Cannot start power hint session: already running");
return false;
}
- LOG_ALWAYS_FATAL_IF(threadIds.empty(), "No thread IDs provided to power hint session!");
- {
- std::lock_guard lock(mHintSessionMutex);
- mHintSession = nullptr;
- mHintSessionThreadIds = threadIds;
-
- auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
- threadIds, mTargetDuration.ns());
-
- if (ret.isOk()) {
- mHintSessionRunning = true;
- mHintSession = ret.value();
- }
- }
- return mHintSessionRunning;
+ return ensurePowerHintSessionRunning();
}
void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 05e4c8b..bbe51cc0 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -25,9 +25,14 @@
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
+// FMQ library in IPower does questionable conversions
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
-#include <compositionengine/impl/OutputCompositionState.h>
#include <powermanager/PowerHalController.h>
+#pragma clang diagnostic pop
+
+#include <compositionengine/impl/OutputCompositionState.h>
#include <scheduler/Time.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
@@ -46,16 +51,15 @@
// Initializes resources that cannot be initialized on construction
virtual void init() = 0;
+ // Used to indicate that power hints can now be reported
virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
- virtual void notifyCpuLoadUp() = 0;
- virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
- // Checks both if it supports and if it's enabled
+ // Checks both if it's supported and if it's enabled; this is thread-safe since its values are
+ // set before onBootFinished, which gates all methods that run on threads other than SF main
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
- virtual bool ensurePowerHintSessionRunning() = 0;
// Sends a power hint that updates to the target work duration for the frame
virtual void updateTargetWorkDuration(Duration targetDuration) = 0;
// Sends a power hint for the actual known work duration at the end of the frame
@@ -63,7 +67,7 @@
// Sets whether the power hint session is enabled
virtual void enablePowerHintSession(bool enabled) = 0;
// Initializes the power hint session
- virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
+ virtual bool startPowerHintSession(std::vector<int32_t>&& threadIds) = 0;
// Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
// Reports the start and end times of a hwc validate call this frame for a given display
@@ -94,6 +98,12 @@
virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
// Sets the target duration for the entire pipeline including the gpu
virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0;
+
+ // --- The following methods may run on threads besides SF main ---
+ // Send a hint about an upcoming increase in the CPU workload
+ virtual void notifyCpuLoadUp() = 0;
+ // Send a hint about the imminent start of a new CPU workload
+ virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
};
namespace impl {
@@ -109,16 +119,13 @@
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
- void notifyCpuLoadUp() override;
- void notifyDisplayUpdateImminentAndCpuReset() override;
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
- bool ensurePowerHintSessionRunning() override;
void updateTargetWorkDuration(Duration targetDuration) override;
void reportActualWorkDuration() override;
void enablePowerHintSession(bool enabled) override;
- bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
- void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
+ bool startPowerHintSession(std::vector<int32_t>&& threadIds) override;
+ void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) override;
void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
TimePoint validateEndTime) override;
void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
@@ -128,13 +135,16 @@
void setExpectedPresentTime(TimePoint expectedPresentTime) override;
void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override;
void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override;
-
void setFrameDelay(Duration frameDelayDuration) override;
void setCommitStart(TimePoint commitStartTime) override;
void setCompositeEnd(TimePoint compositeEndTime) override;
void setDisplays(std::vector<DisplayId>& displayIds) override;
void setTotalFrameTargetWorkDuration(Duration targetDuration) override;
+ // --- The following methods may run on threads besides SF main ---
+ void notifyCpuLoadUp() override;
+ void notifyDisplayUpdateImminentAndCpuReset() override;
+
private:
friend class PowerAdvisorTest;
@@ -220,6 +230,7 @@
// this normalizes them together and takes the max of the two
Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);
+ bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex);
std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
// Current frame's delay
@@ -242,9 +253,10 @@
// Ensure powerhal connection is initialized
power::PowerHalController& getPowerHal();
+ // These variables are set before mBootFinished and never mutated after, so it's safe to access
+ // from threaded methods.
std::optional<bool> mHintSessionEnabled;
std::optional<bool> mSupportsHintSession;
- bool mHintSessionRunning = false;
std::mutex mHintSessionMutex;
std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
@@ -261,6 +273,11 @@
// The list of thread ids, stored so we can restart the session from this class if needed
std::vector<int32_t> mHintSessionThreadIds;
Duration mLastTargetDurationSent = kDefaultTargetDuration;
+
+ // Used to manage the execution ordering of reportActualWorkDuration for concurrency testing
+ std::promise<bool> mDelayReportActualMutexAcquisitonPromise;
+ bool mTimingTestingMode = false;
+
// Whether we should emit ATRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index d62075e..4b5a68c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -175,7 +175,7 @@
return NO_ERROR;
}
-status_t VirtualDisplaySurface::advanceFrame() {
+status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) {
if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -223,7 +223,7 @@
}
// TODO: Correctly propagate the dataspace from GL composition
result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
- ui::Dataspace::UNKNOWN);
+ ui::Dataspace::UNKNOWN, hdrSdrRatio);
}
return result;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index be06e2b..90426f7 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -84,7 +84,7 @@
//
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType);
- virtual status_t advanceFrame();
+ virtual status_t advanceFrame(float hdrSdrRatio);
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
virtual void resizeBuffers(const ui::Size&) override;
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index e55cd3e..55b395b 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -18,41 +18,26 @@
#include "DisplayDevice.h"
namespace android {
-namespace {
-
-RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform,
- const DisplayDevice& display) {
- if (!useIdentityTransform) {
- return RenderArea::RotationFlags::ROT_0;
- }
-
- return ui::Transform::toRotationFlags(display.getOrientation());
-}
-
-} // namespace
std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
const Rect& sourceCrop, ui::Size reqSize,
ui::Dataspace reqDataSpace,
- bool useIdentityTransform,
bool hintForSeamlessTransition,
bool allowSecureLayers) {
if (auto display = displayWeak.promote()) {
// Using new to access a private constructor.
return std::unique_ptr<DisplayRenderArea>(
new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
- useIdentityTransform, hintForSeamlessTransition,
- allowSecureLayers));
+ hintForSeamlessTransition, allowSecureLayers));
}
return nullptr;
}
DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace reqDataSpace,
- bool useIdentityTransform, bool hintForSeamlessTransition,
- bool allowSecureLayers)
+ bool hintForSeamlessTransition, bool allowSecureLayers)
: RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition,
- allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
+ allowSecureLayers),
mDisplay(std::move(display)),
mSourceCrop(sourceCrop) {}
@@ -73,17 +58,7 @@
if (mSourceCrop.isEmpty()) {
return mDisplay->getLayerStackSpaceRect();
}
-
- // Correct for the orientation when the screen capture request contained
- // useIdentityTransform. This will cause the rotation flag to be non 0 since
- // it needs to rotate based on the screen orientation to allow the screenshot
- // to be taken in the ROT_0 orientation
- const auto flags = getRotationFlags();
- int width = mDisplay->getLayerStackSpaceRect().getWidth();
- int height = mDisplay->getLayerStackSpaceRect().getHeight();
- ui::Transform rotation;
- rotation.set(flags, width, height);
- return rotation.transform(mSourceCrop);
+ return mSourceCrop;
}
} // namespace android
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index 9a4981c..4555a9e 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -29,7 +29,6 @@
public:
static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace,
- bool useIdentityTransform,
bool hintForSeamlessTransition,
bool allowSecureLayers = true);
@@ -40,8 +39,7 @@
private:
DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace, bool useIdentityTransform, bool hintForSeamlessTransition,
- bool allowSecureLayers = true);
+ ui::Dataspace, bool hintForSeamlessTransition, bool allowSecureLayers = true);
const sp<const DisplayDevice> mDisplay;
const Rect mSourceCrop;
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
deleted file mode 100644
index f8ad8f6..0000000
--- a/services/surfaceflinger/FlagManager.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include "FlagManager.h"
-
-#include <SurfaceFlingerProperties.sysprop.h>
-#include <android-base/parsebool.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <renderengine/RenderEngine.h>
-#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;
-
-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) {
- base::ParseBoolResult parseResult = base::ParseBool(str);
- switch (parseResult) {
- case base::ParseBoolResult::kTrue:
- return std::make_optional(true);
- case base::ParseBoolResult::kFalse:
- return std::make_optional(false);
- case base::ParseBoolResult::kError:
- return std::nullopt;
- }
-}
-} // namespace
-
-std::string FlagManager::getServerConfigurableFlag(const std::string& experimentFlagName) const {
- return server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
- experimentFlagName, "");
-}
-
-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;
- }
- 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);
-}
-
-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);
-}
-
-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);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
deleted file mode 100644
index e834142..0000000
--- a/services/surfaceflinger/FlagManager.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <optional>
-#include <string>
-
-namespace android {
-// Manages flags for SurfaceFlinger, including default values, system properties, and Mendel
-// experiment configuration values.
-class FlagManager {
-public:
- FlagManager() = default;
- virtual ~FlagManager();
- void dump(std::string& result) const;
-
- int64_t demo_flag() const;
-
- bool use_adpf_cpu_hint() const;
-
- bool use_skia_tracing() const;
-
-private:
- friend class FlagManagerTest;
-
- // Wrapper for mocking in test.
- virtual std::string getServerConfigurableFlag(const std::string& experimentFlagName) const;
-
- template <typename T>
- T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
- T defaultValue) const;
-};
-} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 2d4ec04..29c9432 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -28,6 +28,7 @@
],
static_libs: [
"libperfetto_client_experimental",
+ "libsurfaceflinger_common",
],
export_include_dirs: ["."],
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 1b1307b..d0e2d7a 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -21,6 +21,7 @@
#include "FrameTimeline.h"
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -279,6 +280,19 @@
return protoJank;
}
+FrameTimelineEvent::JankSeverityType toProto(JankSeverityType jankSeverityType) {
+ switch (jankSeverityType) {
+ case JankSeverityType::Unknown:
+ return FrameTimelineEvent::SEVERITY_UNKNOWN;
+ case JankSeverityType::None:
+ return FrameTimelineEvent::SEVERITY_NONE;
+ case JankSeverityType::Partial:
+ return FrameTimelineEvent::SEVERITY_PARTIAL;
+ case JankSeverityType::Full:
+ return FrameTimelineEvent::SEVERITY_FULL;
+ }
+}
+
// Returns the smallest timestamp from the set of predictions and actuals.
nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions,
TimelineItem actuals) {
@@ -376,6 +390,22 @@
mGpuComposition = true;
}
+// TODO(b/316171339): migrate from perfetto side
+bool SurfaceFrame::isSelfJanky() const {
+ int32_t jankType = getJankType().value_or(JankType::None);
+
+ if (jankType == JankType::None) {
+ return false;
+ }
+
+ int32_t jankBitmask = JankType::AppDeadlineMissed | JankType::Unknown;
+ if (jankType & jankBitmask) {
+ return true;
+ }
+
+ return false;
+}
+
std::optional<int32_t> SurfaceFrame::getJankType() const {
std::scoped_lock lock(mMutex);
if (mPresentState == PresentState::Dropped) {
@@ -388,6 +418,15 @@
return mJankType;
}
+std::optional<JankSeverityType> SurfaceFrame::getJankSeverityType() const {
+ std::scoped_lock lock(mMutex);
+ if (mActuals.presentTime == 0) {
+ // Frame hasn't been presented yet.
+ return std::nullopt;
+ }
+ return mJankSeverityType;
+}
+
nsecs_t SurfaceFrame::getBaseTime() const {
std::scoped_lock lock(mMutex);
return getMinTime(mPredictionState, mPredictions, mActuals);
@@ -504,10 +543,11 @@
}
void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
- nsecs_t& deadlineDelta) {
+ Fps displayFrameRenderRate, nsecs_t& deadlineDelta) {
if (mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
// Cannot do any classification for invalid present time.
mJankType = JankType::Unknown;
+ mJankSeverityType = JankSeverityType::Unknown;
deadlineDelta = -1;
return;
}
@@ -518,6 +558,7 @@
// reasonable app, so prediction expire would mean a huge scheduling delay.
mJankType = mPresentState != PresentState::Presented ? JankType::Dropped
: JankType::AppDeadlineMissed;
+ mJankSeverityType = JankSeverityType::Unknown;
deadlineDelta = -1;
return;
}
@@ -542,6 +583,11 @@
if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
+ // Jank that is missing by less than the render rate period is classified as partial jank,
+ // otherwise it is a full jank.
+ mJankSeverityType = std::abs(presentDelta) < displayFrameRenderRate.getPeriodNsecs()
+ ? JankSeverityType::Partial
+ : JankSeverityType::Full;
} else {
mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
}
@@ -612,6 +658,7 @@
mJankType = JankType::Dropped;
// Since frame was not presented, lets drop any present value
mActuals.presentTime = 0;
+ mJankSeverityType = JankSeverityType::Unknown;
}
}
@@ -624,7 +671,7 @@
mActuals.presentTime = presentTime;
nsecs_t deadlineDelta = 0;
- classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta);
+ classifyJankLocked(displayFrameJankType, refreshRate, displayFrameRenderRate, deadlineDelta);
if (mPredictionState != PredictionState::None) {
// Only update janky frames if the app used vsync predictions
@@ -717,6 +764,7 @@
actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState));
actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer);
+ actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType));
});
// Actual timeline end
@@ -909,6 +957,7 @@
// Cannot do jank classification with expired predictions or invalid signal times. Set the
// deltas to 0 as both negative and positive deltas are used as real values.
mJankType = JankType::Unknown;
+ mJankSeverityType = JankSeverityType::Unknown;
deadlineDelta = 0;
deltaToVsync = 0;
if (!presentTimeValid) {
@@ -940,6 +989,11 @@
if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
+ // Jank that is missing by less than the render rate period is classified as partial jank,
+ // otherwise it is a full jank.
+ mJankSeverityType = std::abs(presentDelta) < mRenderRate.getPeriodNsecs()
+ ? JankSeverityType::Partial
+ : JankSeverityType::Full;
} else {
mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
}
@@ -1074,6 +1128,70 @@
});
}
+void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousPredictionPresentTime) const {
+ nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0;
+ const constexpr float kThresh = 0.5f;
+ const constexpr float kRange = 1.5f;
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ if (previousPredictionPresentTime != 0 &&
+ static_cast<float>(mSurfaceFlingerPredictions.presentTime -
+ previousPredictionPresentTime) >=
+ static_cast<float>(mRenderRate.getPeriodNsecs()) * kRange &&
+ static_cast<float>(surfaceFrame->getPredictions().presentTime) <=
+ (static_cast<float>(mSurfaceFlingerPredictions.presentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
+ static_cast<float>(surfaceFrame->getPredictions().presentTime) >=
+ (static_cast<float>(previousPredictionPresentTime) -
+ kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
+ // sf skipped frame is not considered if app is self janked
+ !surfaceFrame->isSelfJanky()) {
+ 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));
+ actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None));
+ });
+
+ // 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();
@@ -1099,6 +1217,7 @@
actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE);
actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState));
+ actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType));
});
// Actual timeline end
@@ -1115,17 +1234,18 @@
});
}
-void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const {
+nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousPredictionPresentTime) const {
if (mSurfaceFrames.empty()) {
// We don't want to trace display frames without any surface frames updates as this cannot
// be janky
- return;
+ return previousPredictionPresentTime;
}
if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
// DisplayFrame should not have an invalid token.
ALOGE("Cannot trace DisplayFrame with invalid token");
- return;
+ return previousPredictionPresentTime;
}
if (mPredictionState == PredictionState::Valid) {
@@ -1138,6 +1258,11 @@
for (auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->trace(mToken, monoBootOffset);
}
+
+ if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) {
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime);
+ }
+ return mSurfaceFlingerPredictions.presentTime;
}
float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
@@ -1228,8 +1353,9 @@
const auto& pendingPresentFence = *mPendingPresentFences.begin();
const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+ displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+ mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime);
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
@@ -1244,9 +1370,10 @@
}
auto& displayFrame = pendingPresentFence.second;
- displayFrame->onPresent(signalTime, mPreviousPresentTime);
- displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
- mPreviousPresentTime = signalTime;
+ displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+ mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime);
+ mPreviousActualPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
--i;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 538ea12..a76f7d4 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -165,9 +165,12 @@
TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode);
~SurfaceFrame() = default;
+ bool isSelfJanky() const;
+
// Returns std::nullopt if the frame hasn't been classified yet.
// Used by both SF and FrameTimeline.
std::optional<int32_t> getJankType() const;
+ std::optional<JankSeverityType> getJankSeverityType() const;
// Functions called by SF
int64_t getToken() const { return mToken; };
@@ -232,7 +235,7 @@
void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
- nsecs_t& deadlineDelta) REQUIRES(mMutex);
+ Fps displayFrameRenderRate, nsecs_t& deadlineDelta) REQUIRES(mMutex);
const int64_t mToken;
const int32_t mInputEventId;
@@ -252,6 +255,8 @@
mutable std::mutex mMutex;
// Bitmask for the type of jank
int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
+ // Enum for the severity of jank
+ JankSeverityType mJankSeverityType GUARDED_BY(mMutex) = JankSeverityType::None;
// Indicates if this frame was composited by the GPU or not
bool mGpuComposition GUARDED_BY(mMutex) = false;
// Refresh rate for this frame.
@@ -378,7 +383,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;
+ nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ nsecs_t previousPredictionPresentTime) 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);
@@ -403,6 +409,7 @@
FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
int32_t getJankType() const { return mJankType; }
+ JankSeverityType getJankSeverityType() const { return mJankSeverityType; }
const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const {
return mSurfaceFrames;
}
@@ -411,6 +418,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);
@@ -432,6 +441,8 @@
PredictionState mPredictionState = PredictionState::None;
// Bitmask for the type of jank
int32_t mJankType = JankType::None;
+ // Enum for the severity of jank
+ JankSeverityType mJankSeverityType = JankSeverityType::None;
// A valid gpu fence indicates that the DisplayFrame was composited by the GPU
std::shared_ptr<FenceTime> mGpuFence = FenceTime::NO_FENCE;
// Enum for the type of present
@@ -499,7 +510,8 @@
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
- nsecs_t mPreviousPresentTime = 0;
+ nsecs_t mPreviousActualPresentTime = 0;
+ nsecs_t mPreviousPredictionPresentTime = 0;
const JankClassificationThresholds mJankClassificationThresholds;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 1e5a6fb..821ac0c 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -190,8 +190,12 @@
return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
}
-LayerHierarchyBuilder::LayerHierarchyBuilder(
- const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+void LayerHierarchyBuilder::init(const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+ mLayerIdToHierarchy.clear();
+ mHierarchies.clear();
+ mRoot = nullptr;
+ mOffscreenRoot = nullptr;
+
mHierarchies.reserve(layers.size());
mLayerIdToHierarchy.reserve(layers.size());
for (auto& layer : layers) {
@@ -202,6 +206,7 @@
onLayerAdded(layer.get());
}
detachHierarchyFromRelativeParent(&mOffscreenRoot);
+ mInitialized = true;
}
void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
@@ -332,7 +337,7 @@
}
}
-void LayerHierarchyBuilder::update(
+void LayerHierarchyBuilder::doUpdate(
const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
// rebuild map
@@ -381,6 +386,32 @@
attachHierarchyToRelativeParent(&mRoot);
}
+void LayerHierarchyBuilder::update(LayerLifecycleManager& layerLifecycleManager) {
+ if (!mInitialized) {
+ ATRACE_NAME("LayerHierarchyBuilder:init");
+ init(layerLifecycleManager.getLayers());
+ } else if (layerLifecycleManager.getGlobalChanges().test(
+ RequestedLayerState::Changes::Hierarchy)) {
+ ATRACE_NAME("LayerHierarchyBuilder:update");
+ doUpdate(layerLifecycleManager.getLayers(), layerLifecycleManager.getDestroyedLayers());
+ } else {
+ return; // nothing to do
+ }
+
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot);
+ while (hasRelZLoop) {
+ ATRACE_NAME("FixRelZLoop");
+ TransactionTraceWriter::getInstance().invoke("relz_loop_detected",
+ /*overwrite=*/false);
+ layerLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ // reinitialize the hierarchy with the updated layer data
+ init(layerLifecycleManager.getLayers());
+ // check if we have any remaining loops
+ hasRelZLoop = mRoot.hasRelZLoop(invalidRelativeRoot);
+ }
+}
+
const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
return mRoot;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index ba2e262..a1c73c3 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -17,6 +17,7 @@
#pragma once
#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerLifecycleManager.h"
#include "RequestedLayerState.h"
#include "ftl/small_vector.h"
@@ -197,9 +198,8 @@
// hierarchy from a list of RequestedLayerState and associated change flags.
class LayerHierarchyBuilder {
public:
- LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
- void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
- const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+ LayerHierarchyBuilder() = default;
+ void update(LayerLifecycleManager& layerLifecycleManager);
LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
const LayerHierarchy& getHierarchy() const;
const LayerHierarchy& getOffscreenHierarchy() const;
@@ -213,14 +213,18 @@
void detachFromRelativeParent(LayerHierarchy*);
void attachHierarchyToRelativeParent(LayerHierarchy*);
void detachHierarchyFromRelativeParent(LayerHierarchy*);
-
+ void init(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+ void doUpdate(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
void onLayerDestroyed(RequestedLayerState* layer);
void updateMirrorLayer(RequestedLayerState* layer);
LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+
std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
LayerHierarchy mRoot{nullptr};
LayerHierarchy mOffscreenRoot{nullptr};
+ bool mInitialized = false;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index a826ec1..0983e7c 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -433,4 +433,15 @@
}
}
+bool LayerLifecycleManager::isLayerSecure(uint32_t layerId) const {
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ return false;
+ }
+
+ if (getLayerFromId(layerId)->flags & layer_state_t::eLayerSecure) {
+ return true;
+ }
+ return isLayerSecure(getLayerFromId(layerId)->parentId);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 9aff78e..330da9a 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -80,6 +80,7 @@
const std::vector<RequestedLayerState*>& getChangedLayers() const;
const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
const RequestedLayerState* getLayerFromId(uint32_t) const;
+ bool isLayerSecure(uint32_t) const;
private:
friend class LayerLifecycleManagerTest;
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..ad5e42b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -365,7 +365,7 @@
return snapshot;
}
-LayerSnapshotBuilder::LayerSnapshotBuilder() : mRootSnapshot(getRootSnapshot()) {}
+LayerSnapshotBuilder::LayerSnapshotBuilder() {}
LayerSnapshotBuilder::LayerSnapshotBuilder(Args args) : LayerSnapshotBuilder() {
args.forceUpdate = ForceUpdateFlags::ALL;
@@ -417,19 +417,20 @@
void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
ATRACE_NAME("UpdateSnapshots");
+ LayerSnapshot rootSnapshot = args.rootSnapshot;
if (args.parentCrop) {
- mRootSnapshot.geomLayerBounds = *args.parentCrop;
+ rootSnapshot.geomLayerBounds = *args.parentCrop;
} else if (args.forceUpdate == ForceUpdateFlags::ALL || args.displayChanges) {
- mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
+ rootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
}
if (args.displayChanges) {
- mRootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren |
+ rootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren |
RequestedLayerState::Changes::Geometry;
}
if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
- mRootSnapshot.changes |=
+ rootSnapshot.changes |=
RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
- mRootSnapshot.clientChanges |= layer_state_t::eReparent;
+ rootSnapshot.clientChanges |= layer_state_t::eReparent;
}
for (auto& snapshot : mSnapshots) {
@@ -444,13 +445,13 @@
// multiple children.
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
LayerHierarchy::Variant::Attached);
- updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot, /*depth=*/0);
+ updateSnapshotsInHierarchy(args, args.root, root, rootSnapshot, /*depth=*/0);
} else {
for (auto& [childHierarchy, variant] : args.root.mChildren) {
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
childHierarchy->getLayer()->id,
variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot, /*depth=*/0);
+ updateSnapshotsInHierarchy(args, *childHierarchy, root, rootSnapshot, /*depth=*/0);
}
}
@@ -459,7 +460,6 @@
updateTouchableRegionCrop(args);
const bool hasUnreachableSnapshots = sortSnapshotsByZ(args);
- clearChanges(mRootSnapshot);
// Destroy unreachable snapshots for clone layers. And destroy snapshots for non-clone
// layers if the layer have been destroyed.
@@ -708,7 +708,7 @@
ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes &
(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
- RequestedLayerState::Changes::AffectsChildren |
+ RequestedLayerState::Changes::AffectsChildren | RequestedLayerState::Changes::Input |
RequestedLayerState::Changes::FrameRate | RequestedLayerState::Changes::GameMode);
snapshot.changes |= parentChanges;
if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
@@ -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);
}
@@ -813,9 +814,12 @@
RequestedLayerState::Changes::Hierarchy) ||
snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
RequestedLayerState::Changes::Hierarchy)) {
- bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy ==
+ const bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy ==
scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren;
- if (!requested.requestedFrameRate.isValid() || shouldOverrideChildren) {
+ const bool propagationAllowed = parentSnapshot.frameRateSelectionStrategy !=
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self;
+ if ((!requested.requestedFrameRate.isValid() && propagationAllowed) ||
+ shouldOverrideChildren) {
snapshot.inheritedFrameRate = parentSnapshot.inheritedFrameRate;
} else {
snapshot.inheritedFrameRate = requested.requestedFrameRate;
@@ -827,12 +831,15 @@
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionStrategyChanged) {
- const auto strategy = scheduler::LayerInfo::convertFrameRateSelectionStrategy(
- requested.frameRateSelectionStrategy);
- snapshot.frameRateSelectionStrategy =
- strategy == scheduler::LayerInfo::FrameRateSelectionStrategy::Self
- ? parentSnapshot.frameRateSelectionStrategy
- : strategy;
+ if (parentSnapshot.frameRateSelectionStrategy ==
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren) {
+ snapshot.frameRateSelectionStrategy =
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren;
+ } else {
+ const auto strategy = scheduler::LayerInfo::convertFrameRateSelectionStrategy(
+ requested.frameRateSelectionStrategy);
+ snapshot.frameRateSelectionStrategy = strategy;
+ }
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) {
@@ -1034,6 +1041,19 @@
snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
+ snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
+ ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+ : parentSnapshot.inputInfo.touchOcclusionMode;
+ if (requested.dropInputMode == gui::DropInputMode::ALL ||
+ parentSnapshot.dropInputMode == gui::DropInputMode::ALL) {
+ snapshot.dropInputMode = gui::DropInputMode::ALL;
+ } else if (requested.dropInputMode == gui::DropInputMode::OBSCURED ||
+ parentSnapshot.dropInputMode == gui::DropInputMode::OBSCURED) {
+ snapshot.dropInputMode = gui::DropInputMode::OBSCURED;
+ } else {
+ snapshot.dropInputMode = gui::DropInputMode::NONE;
+ }
+
updateVisibility(snapshot, snapshot.isVisible);
if (!needsInputInfo(snapshot, requested)) {
return;
@@ -1057,18 +1077,6 @@
}
snapshot.inputInfo.alpha = snapshot.color.a;
- snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
- ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
- : parentSnapshot.inputInfo.touchOcclusionMode;
- if (requested.dropInputMode == gui::DropInputMode::ALL ||
- parentSnapshot.dropInputMode == gui::DropInputMode::ALL) {
- snapshot.dropInputMode = gui::DropInputMode::ALL;
- } else if (requested.dropInputMode == gui::DropInputMode::OBSCURED ||
- parentSnapshot.dropInputMode == gui::DropInputMode::OBSCURED) {
- snapshot.dropInputMode = gui::DropInputMode::OBSCURED;
- } else {
- snapshot.dropInputMode = gui::DropInputMode::NONE;
- }
handleDropInputMode(snapshot, parentSnapshot);
@@ -1095,6 +1103,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/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 1506913..1cec018 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -33,6 +33,9 @@
// The builder also uses a fast path to update
// snapshots when there are only buffer updates.
class LayerSnapshotBuilder {
+private:
+ static LayerSnapshot getRootSnapshot();
+
public:
enum class ForceUpdateFlags {
NONE,
@@ -55,6 +58,7 @@
const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
bool skipRoundCornersWhenProtected = false;
+ LayerSnapshot rootSnapshot = getRootSnapshot();
};
LayerSnapshotBuilder();
@@ -87,7 +91,6 @@
private:
friend class LayerSnapshotTest;
- static LayerSnapshot getRootSnapshot();
// return true if we were able to successfully update the snapshots via
// the fast path.
@@ -130,7 +133,6 @@
std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
mNeedsTouchableRegionCrop;
std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
- LayerSnapshot mRootSnapshot;
bool mResortSnapshots = false;
int mNumInterestingSnapshots = 0;
};
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 0e49b75..209df79 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -98,7 +98,6 @@
z = 0;
layerStack = ui::DEFAULT_LAYER_STACK;
transformToDisplayInverse = false;
- dataspace = ui::Dataspace::UNKNOWN;
desiredHdrSdrRatio = 1.f;
currentHdrSdrRatio = 1.f;
dataspaceRequested = false;
@@ -126,7 +125,7 @@
frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
frameRateCategorySmoothSwitchOnly = false;
frameRateSelectionStrategy =
- static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate);
dataspace = ui::Dataspace::V0_SRGB;
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
@@ -418,6 +417,8 @@
if (!obj.handleAlive) out << " handleNotAlive";
if (obj.requestedFrameRate.isValid())
out << " requestedFrameRate: {" << obj.requestedFrameRate << "}";
+ if (obj.dropInputMode != gui::DropInputMode::NONE)
+ out << " dropInputMode=" << static_cast<uint32_t>(obj.dropInputMode);
return out;
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 700baa2..c8b1059 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;
@@ -165,6 +166,7 @@
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
+ mDrawingState.previousFrameNumber = 0;
mDrawingState.barrierFrameNumber = 0;
mDrawingState.producerId = 0;
mDrawingState.barrierProducerId = 0;
@@ -191,7 +193,7 @@
mDrawingState.dropInputMode = gui::DropInputMode::NONE;
mDrawingState.dimmingEnabled = true;
mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
- mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Self;
+ mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Propagate;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -1271,14 +1273,15 @@
auto now = systemTime();
*transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
- // The frame rate is propagated to the children
+ // The frame rate is propagated to the children by default, but some properties may override it.
bool childrenHaveFrameRate = false;
+ const bool overrideChildrenFrameRate = overrideChildren || shouldOverrideChildrenFrameRate();
+ const bool canPropagateFrameRate = shouldPropagateFrameRate() || overrideChildrenFrameRate;
for (const sp<Layer>& child : mCurrentChildren) {
childrenHaveFrameRate |=
- child->propagateFrameRateForLayerTree(frameRate,
- overrideChildren ||
- shouldOverrideChildrenFrameRate(),
- transactionNeeded);
+ child->propagateFrameRateForLayerTree(canPropagateFrameRate ? frameRate
+ : FrameRate(),
+ overrideChildrenFrameRate, transactionNeeded);
}
// If we don't have a valid frame rate specification, but the children do, we set this
@@ -1716,10 +1719,18 @@
StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
crop.bottom);
const auto frameRate = snapshot.frameRate;
+ std::string frameRateStr;
+ if (frameRate.vote.rate.isValid()) {
+ StringAppendF(&frameRateStr, "%.2f", frameRate.vote.rate.getValue());
+ }
if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(),
+ StringAppendF(&result, "%6s %15s %17s", frameRateStr.c_str(),
ftl::enum_string(frameRate.vote.type).c_str(),
ftl::enum_string(frameRate.vote.seamlessness).c_str());
+ } else if (frameRate.category != FrameRateCategory::Default) {
+ StringAppendF(&result, "%6s %15s %17s", frameRateStr.c_str(),
+ (std::string("Cat::") + ftl::enum_string(frameRate.category)).c_str(),
+ ftl::enum_string(frameRate.vote.seamlessness).c_str());
} else {
result.append(41, ' ');
}
@@ -2591,6 +2602,9 @@
}
}
+ Rect bufferSize = getBufferSize(getDrawingState());
+ info.contentSize = Size(bufferSize.width(), bufferSize.height());
+
return info;
}
@@ -2673,6 +2687,7 @@
}
void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId) {
+ if (mFlinger->mLayerLifecycleManagerEnabled) return;
mSnapshot->path.id = clonedFrom->getSequence();
mSnapshot->path.mirrorRootIds.emplace_back(mirrorRootId);
@@ -2931,7 +2946,6 @@
break;
}
}
-
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
@@ -2940,6 +2954,10 @@
if (mBufferInfo.mBuffer) {
mPreviouslyPresentedLayerStacks.push_back(layerStack);
}
+
+ if (mDrawingState.frameNumber > 0) {
+ mDrawingState.previousFrameNumber = mDrawingState.frameNumber;
+ }
}
void Layer::onSurfaceFrameCreated(
@@ -3144,6 +3162,7 @@
void Layer::resetDrawingStateBufferInfo() {
mDrawingState.producerId = 0;
mDrawingState.frameNumber = 0;
+ mDrawingState.previousFrameNumber = 0;
mDrawingState.releaseBufferListener = nullptr;
mDrawingState.buffer = nullptr;
mDrawingState.acquireFence = sp<Fence>::make(-1);
@@ -3246,7 +3265,7 @@
// If the layer had been updated a TextureView, this would make sure the present time could be
// same to TextureView update when it's a small dirty, and get the correct heuristic rate.
- if (mFlinger->mScheduler->supportSmallDirtyDetection()) {
+ if (mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) {
if (mDrawingState.useVsyncIdForRefreshRateSelection) {
mUsedVsyncIdForRefreshRateSelection = true;
}
@@ -3279,7 +3298,7 @@
}
}
- if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
+ if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) {
return static_cast<nsecs_t>(0);
}
@@ -3420,6 +3439,7 @@
// If this transaction set an acquire fence on this layer, set its acquire time
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
+ handle->previousFrameNumber = mDrawingState.previousFrameNumber;
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
@@ -3625,7 +3645,7 @@
// to upsert RenderEngine's caches. Put in a special workaround to be backwards
// compatible with old vendors, with a ticking clock.
static const int32_t kVendorVersion =
- base::GetIntProperty("ro.vndk.version", __ANDROID_API_FUTURE__);
+ base::GetIntProperty("ro.board.api_level", __ANDROID_API_FUTURE__);
if (const auto format =
static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
mBufferInfo.mBuffer->getPixelFormat());
@@ -4024,10 +4044,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 +4250,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
@@ -4308,7 +4336,6 @@
prepareBasicGeometryCompositionState();
prepareGeometryCompositionState();
snapshot->roundedCorner = getRoundedCornerState();
- snapshot->stretchEffect = getStretchEffect();
snapshot->transformedBounds = mScreenBounds;
if (mEffectiveShadowRadius > 0.f) {
snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings;
@@ -4414,7 +4441,7 @@
void Layer::setIsSmallDirty(const Region& damageRegion,
const ui::Transform& layerToDisplayTransform) {
mSmallDirty = false;
- if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
+ if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) {
return;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index dd91adc..c772e0e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -138,6 +138,7 @@
ui::Dataspace dataspace;
uint64_t frameNumber;
+ uint64_t previousFrameNumber;
// high watermark framenumber to use to check for barriers to protect ourselves
// from out of order transactions
uint64_t barrierFrameNumber;
@@ -342,6 +343,8 @@
//
ui::Dataspace getDataSpace() const;
+ virtual bool isFrontBuffered() const;
+
virtual sp<LayerFE> getCompositionEngineLayerFE() const;
virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
@@ -433,13 +436,10 @@
void updateCloneBufferInfo();
uint64_t mPreviousFrameNumber = 0;
- /*
- * called after composition.
- * returns true if the layer latched a new buffer this frame.
- */
- void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/,
- const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming&);
+ void onCompositionPresented(const DisplayDevice*,
+ const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+ const std::shared_ptr<FenceTime>& /*presentFence*/,
+ const CompositorTiming&);
// If a buffer was replaced this frame, release the former buffer
void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
@@ -915,14 +915,13 @@
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) {
@@ -1179,6 +1178,10 @@
FrameRateSelectionStrategy::OverrideChildren;
}
+ bool shouldPropagateFrameRate() const {
+ return getDrawingState().frameRateSelectionStrategy != FrameRateSelectionStrategy::Self;
+ }
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
// a transform from the current layer coordinate space to display(screen) coordinate space.
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index f25619a..2dbcb84 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -208,9 +208,15 @@
// activeBuffer, then we need to return LayerSettings.
return;
}
- const bool blackOutLayer =
- (mSnapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
- ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) && !targetSettings.isSecure);
+ bool blackOutLayer;
+ if (FlagManager::getInstance().display_protected()) {
+ blackOutLayer = (mSnapshot->hasProtectedContent && !targetSettings.isProtected) ||
+ (mSnapshot->isSecure && !targetSettings.isSecure);
+ } else {
+ blackOutLayer = (mSnapshot->hasProtectedContent && !targetSettings.isProtected) ||
+ ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) &&
+ !targetSettings.isSecure);
+ }
const bool bufferCanBeUsedAsHwTexture =
mSnapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 42676c6..b960e33 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -16,10 +16,10 @@
#include <algorithm>
+#include <common/FlagManager.h>
#include "Client.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/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 8f658d5..c888ccc 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -276,13 +276,11 @@
}
const Rect sampledBounds = sampleRegion.bounds();
- constexpr bool kUseIdentityTransform = false;
constexpr bool kHintForSeamlessTransition = false;
SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
- ui::Dataspace::V0_SRGB, kUseIdentityTransform,
- kHintForSeamlessTransition);
+ ui::Dataspace::V0_SRGB, kHintForSeamlessTransition);
});
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
@@ -376,10 +374,11 @@
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
+ constexpr bool kIsProtected = false;
if (const auto fenceResult =
mFlinger.captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, buffer,
- kRegionSampling, kGrayscale, nullptr)
+ kRegionSampling, kGrayscale, kIsProtected, nullptr)
.get();
fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 71b85bd..5de148e 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -18,20 +18,16 @@
// physical render area.
class RenderArea {
public:
- using RotationFlags = ui::Transform::RotationFlags;
-
enum class CaptureFill {CLEAR, OPAQUE};
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
- bool hintForSeamlessTransition, bool allowSecureLayers = false,
- RotationFlags rotation = ui::Transform::ROT_0)
+ bool hintForSeamlessTransition, bool allowSecureLayers = false)
: mAllowSecureLayers(allowSecureLayers),
mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
- mRotationFlags(rotation),
mHintForSeamlessTransition(hintForSeamlessTransition) {}
static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
@@ -72,9 +68,6 @@
// on the display).
virtual Rect getSourceCrop() const = 0;
- // Returns the rotation of the source crop and the layers.
- RotationFlags getRotationFlags() const { return mRotationFlags; }
-
// Returns the size of the physical render area.
int getReqWidth() const { return mReqSize.width; }
int getReqHeight() const { return mReqSize.height; }
@@ -103,7 +96,6 @@
const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
- const RotationFlags mRotationFlags;
const bool mHintForSeamlessTransition;
};
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 6d2586a..d714848 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -21,6 +21,7 @@
"libui",
"libutils",
],
+ static_libs: ["libsurfaceflinger_common"],
}
cc_library_headers {
@@ -61,5 +62,9 @@
"libgmock",
"libgtest",
"libscheduler",
+ "libsurfaceflingerflags_test",
+ ],
+ shared_libs: [
+ "server_configurable_flags",
],
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9a55c94..c80c8fd 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,6 +43,7 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include <common/FlagManager.h>
#include <scheduler/VsyncConfig.h>
#include "DisplayHardware/DisplayMode.h"
#include "FrameTimeline.h"
@@ -99,6 +100,11 @@
case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
to_string(event.header.displayId).c_str(), event.modeChange.modeId);
+ case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ return StringPrintf("HdcpLevelsChange{displayId=%s, connectedLevel=%d, maxLevel=%d}",
+ to_string(event.header.displayId).c_str(),
+ event.hdcpLevelsChange.connectedLevel,
+ event.hdcpLevelsChange.maxLevel);
default:
return "Event{}";
}
@@ -169,6 +175,20 @@
}};
}
+DisplayEventReceiver::Event makeHdcpLevelsChange(PhysicalDisplayId displayId,
+ int32_t connectedLevel, int32_t maxLevel) {
+ return DisplayEventReceiver::Event{
+ .header =
+ DisplayEventReceiver::Event::Header{
+ .type = DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE,
+ .displayId = displayId,
+ .timestamp = systemTime(),
+ },
+ .hdcpLevelsChange.connectedLevel = connectedLevel,
+ .hdcpLevelsChange.maxLevel = maxLevel,
+ };
+}
+
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
@@ -300,7 +320,7 @@
mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
}
sp<EventThreadConnection> EventThread::createEventConnection(
@@ -308,7 +328,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));
}
@@ -441,6 +461,14 @@
mCondition.notify_all();
}
+void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ mPendingEvents.push_back(makeHdcpLevelsChange(displayId, connectedLevel, maxLevel));
+ mCondition.notify_all();
+}
+
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
@@ -500,7 +528,7 @@
const auto scheduleResult =
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback");
} else {
mVsyncRegistration.cancel();
@@ -556,6 +584,9 @@
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
return true;
+ case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ return true;
+
case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
return connection->mEventRegistration.test(
gui::ISurfaceComposer::EventRegistration::modeChanged);
@@ -756,7 +787,7 @@
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .earliestVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns()});
}
return oldRegistration;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 7842318..8970103 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -131,6 +131,9 @@
const sp<EventThreadConnection>& connection) const = 0;
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
+
+ virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) = 0;
};
struct IEventThreadCallback {
@@ -177,6 +180,9 @@
void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
+ void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) override;
+
private:
friend EventThreadTest;
diff --git a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
index 405c982..d8c408f 100644
--- a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
+++ b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
@@ -29,6 +29,8 @@
ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
// content properly. Any other value will result in a pull down.
+ Gte, // Layer needs greater than or equal to the frame rate.
+
NoVote, // Layer doesn't have any requirements for the refresh rate and
// should not be considered when the display refresh rate is determined.
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
index cb9bfe9..82af61a 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -15,6 +15,7 @@
*/
#include "FrameRateOverrideMappings.h"
+#include <common/FlagManager.h>
namespace android::scheduler {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -30,7 +31,7 @@
}
}
- {
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
const auto iter = mFrameRateOverridesFromGameManager.find(uid);
if (iter != mFrameRateOverridesFromGameManager.end()) {
return iter->second;
@@ -61,10 +62,13 @@
for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
}
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
- if (std::find_if(overrides.begin(), overrides.end(),
- [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
- overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
+ if (std::find_if(overrides.begin(), overrides.end(),
+ [uid = uid](auto i) { return i.uid == uid; }) == overrides.end()) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
}
}
@@ -93,7 +97,9 @@
if (!hasOverrides) return;
dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent);
- dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+ }
dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor);
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 069d89b..5ce883c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -31,6 +31,7 @@
#include <string>
#include <utility>
+#include <common/FlagManager.h>
#include "../Layer.h"
#include "EventThread.h"
#include "LayerInfo.h"
@@ -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;
@@ -235,6 +266,7 @@
if (isLayerActive(*info, threshold)) {
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
+
const auto voteType = [&]() {
switch (frameRate.vote.type) {
case Layer::FrameRateCompatibility::Default:
@@ -247,15 +279,45 @@
return LayerVoteType::NoVote;
case Layer::FrameRateCompatibility::Exact:
return LayerVoteType::ExplicitExact;
+ case Layer::FrameRateCompatibility::Gte:
+ return LayerVoteType::ExplicitGte;
}
}();
- if (frameRate.isValid()) {
- const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
- frameRate.category});
+ if (FlagManager::getInstance().game_default_frame_rate()) {
+ // Determine the layer frame rate considering the following priorities:
+ // 1. Game mode intervention frame rate override
+ // 2. setFrameRate vote
+ // 3. Game default frame rate override
+
+ const auto& [gameModeFrameRateOverride, gameDefaultFrameRateOverride] =
+ getGameFrameRateOverrideLocked(info->getOwnerUid());
+
+ const auto gameFrameRateOverrideVoteType =
+ info->isVisible() ? LayerVoteType::ExplicitDefault : LayerVoteType::NoVote;
+
+ const auto setFrameRateVoteType =
+ info->isVisible() ? voteType : LayerVoteType::NoVote;
+
+ if (gameModeFrameRateOverride.isValid()) {
+ info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
+ } else if (frameRate.isValid()) {
+ info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate,
+ frameRate.vote.seamlessness, frameRate.category});
+ } else if (gameDefaultFrameRateOverride.isValid()) {
+ info->setLayerVote(
+ {gameFrameRateOverrideVoteType, gameDefaultFrameRateOverride});
+ } else {
+ info->resetLayerVote();
+ }
} else {
- info->resetLayerVote();
+ if (frameRate.isValid()) {
+ const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
+ info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
+ frameRate.category});
+ } else {
+ info->resetLayerVote();
+ }
}
it++;
@@ -314,4 +376,56 @@
return isSmallDirty;
}
+void LayerHistory::updateGameModeFrameRateOverride(FrameRateOverride frameRateOverride) {
+ const uid_t uid = frameRateOverride.uid;
+ std::lock_guard lock(mLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mGameFrameRateOverride[uid].first = Fps::fromValue(frameRateOverride.frameRateHz);
+ } else {
+ if (mGameFrameRateOverride[uid].second.getValue() == 0.f) {
+ mGameFrameRateOverride.erase(uid);
+ } else {
+ mGameFrameRateOverride[uid].first = Fps();
+ }
+ }
+}
+
+void LayerHistory::updateGameDefaultFrameRateOverride(FrameRateOverride frameRateOverride) {
+ const uid_t uid = frameRateOverride.uid;
+ std::lock_guard lock(mLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mGameFrameRateOverride[uid].second = Fps::fromValue(frameRateOverride.frameRateHz);
+ } else {
+ if (mGameFrameRateOverride[uid].first.getValue() == 0.f) {
+ mGameFrameRateOverride.erase(uid);
+ } else {
+ mGameFrameRateOverride[uid].second = Fps();
+ }
+ }
+}
+
+std::pair<Fps, Fps> LayerHistory::getGameFrameRateOverride(uid_t uid) const {
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ return std::pair<Fps, Fps>();
+ }
+
+ std::lock_guard lock(mLock);
+
+ return getGameFrameRateOverrideLocked(uid);
+}
+
+std::pair<Fps, Fps> LayerHistory::getGameFrameRateOverrideLocked(uid_t uid) const {
+ if (!FlagManager::getInstance().game_default_frame_rate()) {
+ return std::pair<Fps, Fps>();
+ }
+
+ const auto it = mGameFrameRateOverride.find(uid);
+
+ if (it == mGameFrameRateOverride.end()) {
+ return std::pair<Fps, Fps>(Fps(), Fps());
+ }
+
+ return it->second;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bac1ec6..930d06c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -43,6 +43,7 @@
class LayerHistory {
public:
+ using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
using LayerVoteType = RefreshRateSelector::LayerVoteType;
static constexpr std::chrono::nanoseconds kMaxPeriodForHistory = 1s;
@@ -73,7 +74,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.
@@ -89,6 +90,15 @@
bool isSmallDirtyArea(uint32_t dirtyArea, float threshold) const;
+ // Updates the frame rate override set by game mode intervention
+ void updateGameModeFrameRateOverride(FrameRateOverride frameRateOverride) EXCLUDES(mLock);
+
+ // Updates the frame rate override set by game default frame rate
+ void updateGameDefaultFrameRateOverride(FrameRateOverride frameRateOverride) EXCLUDES(mLock);
+
+ std::pair<Fps, Fps> getGameFrameRateOverride(uid_t uid) const EXCLUDES(mLock);
+ std::pair<Fps, Fps> getGameFrameRateOverrideLocked(uid_t uid) const REQUIRES(mLock);
+
private:
friend class LayerHistoryTest;
friend class LayerHistoryIntegrationTest;
@@ -137,6 +147,13 @@
// Whether a mode change is in progress or not
std::atomic<bool> mModeChangePending = false;
+
+ // A list to look up the game frame rate overrides
+ // Each entry includes:
+ // 1. the uid of the app
+ // 2. a pair of game mode intervention frame frame and game default frame rate override
+ // set to 0.0 if there is no such override
+ std::map<uid_t, std::pair<Fps, Fps>> mGameFrameRateOverride GUARDED_BY(mLock);
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 36f2475..9c4f7a5 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -62,6 +62,10 @@
mLastAnimationTime = std::max(lastPresentTime, now);
break;
case LayerUpdateType::SetFrameRate:
+ if (FlagManager::getInstance().vrr_config()) {
+ break;
+ }
+ FALLTHROUGH_INTENDED;
case LayerUpdateType::Buffer:
FrameTimeData frameTime = {.presentTime = lastPresentTime,
.queueTime = mLastUpdatedTime,
@@ -75,6 +79,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())
@@ -272,17 +280,16 @@
if (const auto averageFrameTime = calculateAverageFrameTime()) {
const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
- const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
- if (refreshRateConsistent) {
- const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate);
+ const auto closestKnownRefreshRate = mRefreshRateHistory.add(refreshRate, now, selector);
+ if (closestKnownRefreshRate.isValid()) {
using fps_approx_ops::operator!=;
// To avoid oscillation, use the last calculated refresh rate if it is close enough.
if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
MARGIN &&
- mLastRefreshRate.reported != knownRefreshRate) {
+ mLastRefreshRate.reported != closestKnownRefreshRate) {
mLastRefreshRate.calculated = refreshRate;
- mLastRefreshRate.reported = knownRefreshRate;
+ mLastRefreshRate.reported = closestKnownRefreshRate;
}
ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(),
@@ -304,19 +311,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 +341,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 +409,10 @@
return mLayerProps->frameRateSelectionPriority;
}
+bool LayerInfo::isFrontBuffered() const {
+ return mLayerProps->isFrontBuffered;
+}
+
FloatRect LayerInfo::getBounds() const {
return mLayerProps->bounds;
}
@@ -413,7 +435,8 @@
mRefreshRates.clear();
}
-bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) {
+Fps LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now,
+ const RefreshRateSelector& selector) {
mRefreshRates.push_back({refreshRate, now});
while (mRefreshRates.size() >= HISTORY_SIZE ||
now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
@@ -428,11 +451,11 @@
ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue());
}
- return isConsistent();
+ return selectRefreshRate(selector);
}
-bool LayerInfo::RefreshRateHistory::isConsistent() const {
- if (mRefreshRates.empty()) return true;
+Fps LayerInfo::RefreshRateHistory::selectRefreshRate(const RefreshRateSelector& selector) const {
+ if (mRefreshRates.empty()) return Fps();
const auto [min, max] =
std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
@@ -440,8 +463,19 @@
return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
});
- const bool consistent =
- max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
+ const auto maxClosestRate = selector.findClosestKnownFrameRate(max->refreshRate);
+ const bool consistent = [&](Fps maxFps, Fps minFps) {
+ if (FlagManager::getInstance().use_known_refresh_rate_for_fps_consistency()) {
+ if (maxFps.getValue() - minFps.getValue() <
+ MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE) {
+ const auto minClosestRate = selector.findClosestKnownFrameRate(minFps);
+ using fps_approx_ops::operator==;
+ return maxClosestRate == minClosestRate;
+ }
+ return false;
+ }
+ return maxFps.getValue() - minFps.getValue() < MARGIN_CONSISTENT_FPS;
+ }(max->refreshRate, min->refreshRate);
if (CC_UNLIKELY(sTraceEnabled)) {
if (!mHeuristicTraceTagData.has_value()) {
@@ -453,7 +487,7 @@
ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
}
- return consistent;
+ return consistent ? maxClosestRate : Fps();
}
FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
@@ -466,6 +500,8 @@
return FrameRateCompatibility::Exact;
case ANATIVEWINDOW_FRAME_RATE_MIN:
return FrameRateCompatibility::Min;
+ case ANATIVEWINDOW_FRAME_RATE_GTE:
+ return FrameRateCompatibility::Gte;
case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
return FrameRateCompatibility::NoVote;
default:
@@ -496,6 +532,8 @@
return FrameRateCategory::Low;
case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL:
return FrameRateCategory::Normal;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT:
+ return FrameRateCategory::HighHint;
case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH:
return FrameRateCategory::High;
default:
@@ -507,10 +545,12 @@
LayerInfo::FrameRateSelectionStrategy LayerInfo::convertFrameRateSelectionStrategy(
int8_t strategy) {
switch (strategy) {
- case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF:
- return FrameRateSelectionStrategy::Self;
+ case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE:
+ return FrameRateSelectionStrategy::Propagate;
case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN:
return FrameRateSelectionStrategy::OverrideChildren;
+ case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF:
+ return FrameRateSelectionStrategy::Self;
default:
LOG_ALWAYS_FATAL("Invalid frame rate selection strategy value %d", strategy);
return FrameRateSelectionStrategy::Self;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 7d3cffa..326e444 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -81,10 +81,11 @@
using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>;
enum class FrameRateSelectionStrategy {
- Self,
+ Propagate,
OverrideChildren,
+ Self,
- ftl_last = OverrideChildren
+ ftl_last = Self
};
// Encapsulates the frame rate specifications of the layer. This information will be used
@@ -174,7 +175,8 @@
bool pendingModeChange, const LayerProps& props);
// Sets an explicit layer vote. This usually comes directly from the application via
- // ANativeWindow_setFrameRate API
+ // ANativeWindow_setFrameRate API. This is also used by Game Default Frame Rate and
+ // Game Mode Intervention Frame Rate.
void setLayerVote(LayerVote vote) { mLayerVote = vote; }
// Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
@@ -182,6 +184,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 +204,7 @@
FrameRate getSetFrameRateVote() const;
bool isVisible() const;
int32_t getFrameRateSelectionPriority() const;
+ bool isFrontBuffered() const;
FloatRect getBounds() const;
ui::Transform getTransform() const;
@@ -261,8 +266,8 @@
// Clears History
void clear();
- // Adds a new refresh rate and returns true if it is consistent
- bool add(Fps refreshRate, nsecs_t now);
+ // Adds a new refresh rate and returns valid refresh rate if it is consistent enough
+ Fps add(Fps refreshRate, nsecs_t now, const RefreshRateSelector&);
private:
friend class LayerHistoryTest;
@@ -282,13 +287,14 @@
std::string average;
};
- bool isConsistent() const;
+ Fps selectRefreshRate(const RefreshRateSelector&) const;
HeuristicTraceTagData makeHeuristicTraceTagData() const;
const std::string mName;
mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
std::deque<RefreshRateData> mRefreshRates;
static constexpr float MARGIN_CONSISTENT_FPS = 1.0;
+ static constexpr float MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE = 5.0;
};
// Represents whether we were able to determine either layer is frequent or infrequent
@@ -360,6 +366,7 @@
LayerInfo::FrameRate setFrameRateVote;
int32_t frameRateSelectionPriority = -1;
bool isSmallDirty = false;
+ bool isFrontBuffered = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 18c0a69..cf8b3bf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -125,7 +125,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
return oldRegistration;
}
@@ -143,7 +143,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
void MessageQueue::waitMessage() {
@@ -196,7 +196,7 @@
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
- .earliestVsync = mVsync.lastCallbackTime.ns()});
+ .lastVsync = mVsync.lastCallbackTime.ns()});
}
auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 1d23fb5..c3709e5 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -36,9 +36,7 @@
#include <scheduler/FrameRateMode.h>
#include <utils/Trace.h>
-#include "../SurfaceFlingerProperties.h"
#include "RefreshRateSelector.h"
-#include "Utils/FlagUtils.h"
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -115,7 +113,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<)) -
@@ -334,6 +332,15 @@
return calculateNonExactMatchingDefaultLayerScoreLocked(displayPeriod, layerPeriod);
}
+ if (layer.vote == LayerVoteType::ExplicitGte) {
+ using fps_approx_ops::operator>=;
+ if (refreshRate >= layer.desiredRefreshRate) {
+ return 1.0f;
+ } else {
+ return calculateDistanceScoreLocked(layer.desiredRefreshRate, refreshRate);
+ }
+ }
+
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
using fps_approx_ops::operator<;
@@ -392,21 +399,32 @@
return 0;
}
-float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const {
- const auto& maxFps = mAppRequestFrameRates.back().fps;
- const float ratio = refreshRate.getValue() / maxFps.getValue();
- // Use ratio^2 to get a lower score the more we get further from peak
+float RefreshRateSelector::calculateDistanceScoreLocked(Fps referenceRate, Fps refreshRate) const {
+ using fps_approx_ops::operator>=;
+ const float ratio = referenceRate >= refreshRate
+ ? refreshRate.getValue() / referenceRate.getValue()
+ : referenceRate.getValue() / refreshRate.getValue();
+ // Use ratio^2 to get a lower score the more we get further from the reference rate.
return ratio * ratio;
}
+float RefreshRateSelector::calculateDistanceScoreFromMaxLocked(Fps refreshRate) const {
+ const auto& maxFps = mAppRequestFrameRates.back().fps;
+ return calculateDistanceScoreLocked(maxFps, refreshRate);
+}
+
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;
if (layer.vote == LayerVoteType::ExplicitCategory) {
+ // HighHint is considered later for touch boost.
+ if (layer.frameRateCategory == FrameRateCategory::HighHint) {
+ return 0.f;
+ }
+
if (getFrameRateCategoryRange(layer.frameRateCategory).includes(refreshRate)) {
return 1.f;
}
@@ -424,7 +442,7 @@
// If the layer wants Max, give higher score to the higher refresh rate
if (layer.vote == LayerVoteType::Max) {
- return calculateDistanceScoreFromMax(refreshRate);
+ return calculateDistanceScoreFromMaxLocked(refreshRate);
}
if (layer.vote == LayerVoteType::ExplicitExact) {
@@ -492,7 +510,9 @@
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
int explicitExact = 0;
+ int explicitGteLayers = 0;
int explicitCategoryVoteLayers = 0;
+ int interactiveLayers = 0;
int seamedFocusedLayers = 0;
int categorySmoothSwitchOnlyLayers = 0;
@@ -516,8 +536,17 @@
case LayerVoteType::ExplicitExact:
explicitExact++;
break;
+ case LayerVoteType::ExplicitGte:
+ explicitGteLayers++;
+ break;
case LayerVoteType::ExplicitCategory:
- explicitCategoryVoteLayers++;
+ if (layer.frameRateCategory == FrameRateCategory::HighHint) {
+ // HighHint does not count as an explicit signal from an app. It may be
+ // be a touch signal.
+ interactiveLayers++;
+ } else {
+ explicitCategoryVoteLayers++;
+ }
if (layer.frameRateCategory == FrameRateCategory::NoPreference) {
// Count this layer for Min vote as well. The explicit vote avoids
// touch boost and idle for choosing a category, while Min vote is for correct
@@ -538,7 +567,7 @@
}
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
- explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0 ||
+ explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0 || explicitGteLayers > 0 ||
explicitCategoryVoteLayers > 0;
const Policy* policy = getCurrentPolicyLocked();
@@ -691,6 +720,7 @@
case LayerVoteType::Max:
case LayerVoteType::ExplicitDefault:
case LayerVoteType::ExplicitExact:
+ case LayerVoteType::ExplicitGte:
case LayerVoteType::ExplicitCategory:
return false;
}
@@ -813,13 +843,14 @@
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
- if (signals.touch && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 &&
+ const bool hasInteraction = signals.touch || interactiveLayers > 0;
+ if (hasInteraction && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 &&
touchBoostForExplicitExact &&
scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
to_string(touchRefreshRates.front().frameRateMode.fps).c_str());
- return {touchRefreshRates, GlobalSignals{.touch = true}};
+ return {touchRefreshRates, GlobalSignals{.touch = signals.touch}};
}
// If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
@@ -973,17 +1004,21 @@
}
ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
- std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
+ ftl::Optional<DisplayModeId> desiredModeIdOpt, bool timerExpired) const {
std::lock_guard lock(mLock);
- const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
- if (desiredActiveModeId) {
- const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
- return FrameRateMode{modePtr->getPeakFps(), ftl::as_non_null(modePtr)};
- }
-
- return getActiveModeLocked();
- }();
+ const auto current =
+ desiredModeIdOpt
+ .and_then([this](DisplayModeId modeId)
+ REQUIRES(mLock) { return mDisplayModes.get(modeId); })
+ .transform([](const DisplayModePtr& modePtr) {
+ return FrameRateMode{modePtr->getPeakFps(), ftl::as_non_null(modePtr)};
+ })
+ .or_else([this] {
+ ftl::FakeGuard guard(mLock);
+ return std::make_optional(getActiveModeLocked());
+ })
+ .value();
const DisplayModePtr& min = mMinRefreshRateModeIt->second;
if (current.modePtr->getId() == min->getId()) {
@@ -1080,7 +1115,7 @@
return;
}
- float score = calculateDistanceScoreFromMax(frameRateMode.fps);
+ float score = calculateDistanceScoreFromMaxLocked(frameRateMode.fps);
if (ascending) {
score = 1.0f / score;
@@ -1489,7 +1524,8 @@
case FrameRateCategory::Normal:
return FpsRange{60_Hz, 90_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 60_Hz};
+ return FpsRange{30_Hz, 30_Hz};
+ case FrameRateCategory::HighHint:
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 545b939..a1a7c28 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -16,9 +16,6 @@
#pragma once
-#include <algorithm>
-#include <numeric>
-#include <set>
#include <type_traits>
#include <utility>
#include <variant>
@@ -42,13 +39,6 @@
using namespace std::chrono_literals;
-enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
-
-inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
- using T = std::underlying_type_t<DisplayModeEvent>;
- return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
-}
-
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
// Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with
@@ -152,6 +142,7 @@
// ExactOrMultiple compatibility
ExplicitExact, // Specific refresh rate that was provided by the app with
// Exact compatibility
+ ExplicitGte, // Greater than or equal to frame rate provided by the app
ExplicitCategory, // Specific frame rate category was provided by the app
ftl_last = ExplicitCategory
@@ -258,9 +249,8 @@
mMaxRefreshRateModeIt->second->getPeakFps()};
}
- ftl::Optional<FrameRateMode> onKernelTimerChanged(
- std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
- EXCLUDES(mLock);
+ ftl::Optional<FrameRateMode> onKernelTimerChanged(ftl::Optional<DisplayModeId> desiredModeIdOpt,
+ bool timerExpired) const EXCLUDES(mLock);
void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
@@ -464,7 +454,11 @@
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
// Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
- float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock);
+ float calculateDistanceScoreFromMaxLocked(Fps refreshRate) const REQUIRES(mLock);
+
+ // Returns the refresh rate score based on its distance from the reference rate.
+ float calculateDistanceScoreLocked(Fps referenceRate, Fps refreshRate) const REQUIRES(mLock);
+
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5b36a5e..27ca17f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -45,13 +45,18 @@
#include <memory>
#include <numeric>
+#include <common/FlagManager.h>
#include "../Layer.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
+#include "RefreshRateStats.h"
+#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
+#include "TimeStats/TimeStats.h"
#include "VSyncTracker.h"
+#include "VsyncConfiguration.h"
#include "VsyncController.h"
#include "VsyncSchedule.h"
@@ -66,11 +71,16 @@
namespace android::scheduler {
Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
- sp<VsyncModulator> modulatorPtr)
- : impl::MessageQueue(compositor),
+ surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : android::impl::MessageQueue(compositor),
mFeatures(features),
- mVsyncModulator(std::move(modulatorPtr)),
- mSchedulerCallback(callback) {}
+ mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)),
+ mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())),
+ mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate,
+ hal::PowerMode::OFF)),
+ mSchedulerCallback(callback),
+ mVsyncTrackerCallback(vsyncTrackerCallback) {}
Scheduler::~Scheduler() {
// MessageQueue depends on VsyncSchedule, so first destroy it.
@@ -90,7 +100,12 @@
using namespace sysprop;
using namespace std::string_literals;
- if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
+ const int32_t defaultTouchTimerValue =
+ FlagManager::getInstance().enable_fro_dependent_features() &&
+ sysprop::enable_frame_rate_override(true)
+ ? 200
+ : 0;
+ if (const int32_t millis = set_touch_timer_ms(defaultTouchTimerValue); millis > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
mTouchTimer.emplace(
"TouchTimer", std::chrono::milliseconds(millis),
@@ -115,10 +130,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>(
+ selectorPtr->getActiveMode().modePtr, mFeatures,
+ [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); },
+ mVsyncTrackerCallback);
registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr));
}
@@ -175,44 +190,54 @@
const FrameTargeter::BeginFrameArgs beginFrameArgs =
{.frameBeginTime = SchedulerClock::now(),
.vsyncId = vsyncId,
- // TODO(b/255601557): Calculate per display.
.expectedVsyncTime = expectedVsyncTime,
- .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration};
+ .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
+ .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration};
- LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId);
- const auto pacesetterId = *mPacesetterDisplayId;
- const auto pacesetterOpt = mDisplays.get(pacesetterId);
+ ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
+ pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr);
- FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr;
- pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr);
+ {
+ FrameTargets targets;
+ targets.try_emplace(pacesetterPtr->displayId, &pacesetterPtr->targeterPtr->target());
- FrameTargets targets;
- targets.try_emplace(pacesetterId, &pacesetterTargeter.target());
+ // TODO (b/256196556): Followers should use the next VSYNC after the frontrunner, not the
+ // pacesetter.
+ // Update expectedVsyncTime, which may have been adjusted by beginFrame.
+ expectedVsyncTime = pacesetterPtr->targeterPtr->target().expectedPresentTime();
- for (const auto& [id, display] : mDisplays) {
- if (id == pacesetterId) continue;
+ for (const auto& [id, display] : mDisplays) {
+ if (id == pacesetterPtr->displayId) continue;
- FrameTargeter& targeter = *display.targeterPtr;
- targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
- targets.try_emplace(id, &targeter.target());
+ auto followerBeginFrameArgs = beginFrameArgs;
+ followerBeginFrameArgs.expectedVsyncTime =
+ display.schedulePtr->vsyncDeadlineAfter(expectedVsyncTime);
+
+ FrameTargeter& targeter = *display.targeterPtr;
+ targeter.beginFrame(followerBeginFrameArgs, *display.schedulePtr);
+ targets.try_emplace(id, &targeter.target());
+ }
+
+ if (!compositor.commit(pacesetterPtr->displayId, targets)) return;
}
- if (!compositor.commit(pacesetterId, targets)) return;
+ // The pacesetter may have changed or been registered anew during commit.
+ pacesetterPtr = pacesetterPtrLocked();
// TODO(b/256196556): Choose the frontrunner display.
FrameTargeters targeters;
- targeters.try_emplace(pacesetterId, &pacesetterTargeter);
+ targeters.try_emplace(pacesetterPtr->displayId, pacesetterPtr->targeterPtr.get());
for (auto& [id, display] : mDisplays) {
- if (id == pacesetterId) continue;
+ if (id == pacesetterPtr->displayId) continue;
FrameTargeter& targeter = *display.targeterPtr;
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 period = pacesetterPtr->targeterPtr->target().expectedFrameDuration();
const auto skipDuration = Duration::fromNs(
static_cast<nsecs_t>(period.ns() * mPacesetterFrameDurationFractionToSkip));
ATRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)",
@@ -221,7 +246,19 @@
mPacesetterFrameDurationFractionToSkip = 0.f;
}
- const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
+ if (FlagManager::getInstance().vrr_config()) {
+ const auto minFramePeriod = pacesetterPtr->schedulePtr->minFramePeriod();
+ const auto presentFenceForPastVsync =
+ pacesetterPtr->targeterPtr->target().presentFenceForPastVsync(minFramePeriod);
+ const auto lastConfirmedPresentTime = presentFenceForPastVsync->getSignalTime();
+ if (lastConfirmedPresentTime != Fence::SIGNAL_TIME_PENDING &&
+ lastConfirmedPresentTime != Fence::SIGNAL_TIME_INVALID) {
+ pacesetterPtr->schedulePtr->getTracker()
+ .onFrameBegin(expectedVsyncTime, TimePoint::fromNs(lastConfirmedPresentTime));
+ }
+ }
+
+ const auto resultsPerDisplay = compositor.composite(pacesetterPtr->displayId, targeters);
compositor.sample();
for (const auto& [id, targeter] : targeters) {
@@ -287,9 +324,10 @@
frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
- auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
- getVsyncSchedule(), tokenManager, *this,
- workDuration, readyDuration);
+ auto eventThread =
+ std::make_unique<android::impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
+ getVsyncSchedule(), tokenManager, *this,
+ workDuration, readyDuration);
auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
handle = createConnection(std::move(eventThread));
@@ -392,6 +430,17 @@
thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
}
+void Scheduler::onHdcpLevelsChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ int32_t connectedLevel, int32_t maxLevel) {
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
+}
+
void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
@@ -464,8 +513,23 @@
thread->setDuration(workDuration, readyDuration);
}
-void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) {
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod);
+void Scheduler::updatePhaseConfiguration(Fps refreshRate) {
+ mRefreshRateStats->setRefreshRate(refreshRate);
+ mVsyncConfiguration->setRefreshRateFps(refreshRate);
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+ refreshRate.getPeriod());
+}
+
+void Scheduler::resetPhaseConfiguration(Fps refreshRate) {
+ // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+ mVsyncModulator->cancelRefreshRateChange();
+
+ mVsyncConfiguration->reset();
+ updatePhaseConfiguration(refreshRate);
+}
+
+void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
+ mRefreshRateStats->setPowerMode(powerMode);
}
void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) {
@@ -495,13 +559,16 @@
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- for (const auto& [id, _] : mDisplays) {
- resyncToHardwareVsyncLocked(id, allowToEnable);
+ for (const auto& [id, display] : mDisplays) {
+ if (display.powerMode != hal::PowerMode::OFF ||
+ !FlagManager::getInstance().multithreaded_present()) {
+ resyncToHardwareVsyncLocked(id, allowToEnable);
+ }
}
}
void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
- std::optional<Fps> refreshRate) {
+ DisplayModePtr modePtr) {
const auto displayOpt = mDisplays.get(id);
if (!displayOpt) {
ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
@@ -510,12 +577,12 @@
const Display& display = *displayOpt;
if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
- if (!refreshRate) {
- refreshRate = display.selectorPtr->getActiveMode().modePtr->getVsyncRate();
+ if (!modePtr) {
+ modePtr = display.selectorPtr->getActiveMode().modePtr.get();
}
- if (refreshRate->isValid()) {
+ if (modePtr->getVsyncRate().isValid()) {
constexpr bool kForce = false;
- display.schedulePtr->startPeriodTransition(refreshRate->getPeriod(), kForce);
+ display.schedulePtr->onDisplayModeChanged(ftl::as_non_null(modePtr), kForce);
}
}
}
@@ -526,7 +593,7 @@
// On main thread to serialize reads/writes of pending hardware VSYNC state.
static_cast<void>(
- schedule([=]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ schedule([=, this]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str());
if (const auto displayOpt = mDisplays.get(id)) {
@@ -564,6 +631,26 @@
display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
}
+Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id,
+ TimePoint currentExpectedPresentTime) const {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ const auto displayOpt = mDisplays.get(id);
+ if (!displayOpt) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return Fps{};
+ }
+ const Display& display = *displayOpt;
+ const nsecs_t threshold =
+ display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2;
+ const nsecs_t nextVsyncTime =
+ display.schedulePtr->getTracker()
+ .nextAnticipatedVSyncTimeFrom(currentExpectedPresentTime.ns() + threshold,
+ currentExpectedPresentTime.ns());
+ return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns());
+}
+
void Scheduler::resync() {
static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
@@ -589,6 +676,7 @@
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
+ ATRACE_NAME(ftl::Concat(__func__, ' ', id.value).c_str());
const auto scheduleOpt =
(ftl::FakeGuard(mDisplayLock), mDisplays.get(id)).and_then([](const Display& display) {
return display.powerMode == hal::PowerMode::OFF
@@ -599,7 +687,8 @@
if (!scheduleOpt) return;
const auto& schedule = scheduleOpt->get();
- if (const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence))) {
+ const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
+ if (needMoreSignals) {
schedule->enableHardwareVsync();
} else {
constexpr bool kDisallow = false;
@@ -640,6 +729,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) {
@@ -809,6 +902,12 @@
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
+ mVsyncConfiguration->dump(dumper.out());
+ dumper.eol();
+
+ mRefreshRateStats->dump(dumper.out());
+ dumper.eol();
+
{
utils::Dumper::Section section(dumper, "Frame Targeting"sv);
@@ -895,9 +994,9 @@
newVsyncSchedulePtr = pacesetter.schedulePtr;
- const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getVsyncRate();
constexpr bool kForce = true;
- newVsyncSchedulePtr->startPeriodTransition(refreshRate.getPeriod(), kForce);
+ newVsyncSchedulePtr->onDisplayModeChanged(pacesetter.selectorPtr->getActiveMode().modePtr,
+ kForce);
}
return newVsyncSchedulePtr;
}
@@ -1164,7 +1263,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) {
@@ -1181,12 +1280,27 @@
mLayerHistory.setDisplayArea(displayArea);
}
-void Scheduler::setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride) {
+void Scheduler::setGameModeFrameRateForUid(FrameRateOverride frameRateOverride) {
if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
return;
}
- mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
+ if (FlagManager::getInstance().game_default_frame_rate()) {
+ // update the frame rate override mapping in LayerHistory
+ mLayerHistory.updateGameModeFrameRateOverride(frameRateOverride);
+ } else {
+ mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
+ }
+}
+
+void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) {
+ if (!FlagManager::getInstance().game_default_frame_rate() ||
+ (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f)) {
+ return;
+ }
+
+ // update the frame rate override mapping in LayerHistory
+ mLayerHistory.updateGameDefaultFrameRateOverride(frameRateOverride);
}
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
@@ -1203,7 +1317,7 @@
}
void Scheduler::setSmallAreaDetectionThreshold(int32_t appId, float threshold) {
- mSmallAreaDetectionAllowMappings.setThesholdForAppId(appId, threshold);
+ mSmallAreaDetectionAllowMappings.setThresholdForAppId(appId, threshold);
}
bool Scheduler::isSmallDirtyArea(int32_t appId, uint32_t dirtyArea) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index b0520a6..f62f1ba 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -33,6 +33,7 @@
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <ftl/fake_guard.h>
+#include <ftl/non_null.h>
#include <ftl/optional.h>
#include <scheduler/Features.h>
#include <scheduler/FrameTargeter.h>
@@ -87,22 +88,30 @@
namespace android {
class FenceTime;
+class TimeStats;
namespace frametimeline {
class TokenManager;
} // namespace frametimeline
+namespace surfaceflinger {
+class Factory;
+} // namespace surfaceflinger
+
namespace scheduler {
using GlobalSignals = RefreshRateSelector::GlobalSignals;
+class RefreshRateStats;
+class VsyncConfiguration;
class VsyncSchedule;
class Scheduler : public IEventThreadCallback, android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
public:
- Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>);
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&,
+ Fps activeRefreshRate, TimeStats&, IVsyncTrackerCallback&);
virtual ~Scheduler();
void startTimers();
@@ -171,6 +180,8 @@
void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
EXCLUDES(mConnectionsLock);
+ void onHdcpLevelsChanged(ConnectionHandle, PhysicalDisplayId, int32_t, int32_t);
+
// Modifies work duration in the event thread.
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
@@ -197,7 +208,10 @@
}
}
- void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod);
+ void updatePhaseConfiguration(Fps);
+ void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext);
+
+ const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
// Sets the render rate for the scheduler to run at.
void setRenderRate(PhysicalDisplayId, Fps);
@@ -209,13 +223,12 @@
// If allowToEnable is true, then hardware vsync will be turned on.
// Otherwise, if hardware vsync is not already enabled then this method will
// no-op.
- // If refreshRate is nullopt, use the existing refresh rate of the display.
+ // If modePtr is nullopt, use the active display mode.
void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable,
- std::optional<Fps> refreshRate = std::nullopt)
- EXCLUDES(mDisplayLock) {
+ DisplayModePtr modePtr = nullptr) EXCLUDES(mDisplayLock) {
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate);
+ resyncToHardwareVsyncLocked(id, allowToEnable, modePtr);
}
void forceNextResync() { mLastResyncTime = 0; }
@@ -233,6 +246,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);
@@ -245,8 +259,10 @@
// Indicates that touch interaction is taking place.
void onTouchHint();
- void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode)
- REQUIRES(kMainThreadContext);
+ void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode) REQUIRES(kMainThreadContext);
+
+ // TODO(b/255635821): Track this per display.
+ void setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode) REQUIRES(kMainThreadContext);
ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const
EXCLUDES(mDisplayLock);
@@ -281,8 +297,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);
@@ -291,7 +307,17 @@
// FrameRateOverride.refreshRateHz == 0 means no preference.
void setPreferredRefreshRateForUid(FrameRateOverride);
- void setGameModeRefreshRateForUid(FrameRateOverride);
+ // Stores the frame rate override that a game should run at set by game interventions.
+ // FrameRateOverride.refreshRateHz == 0 means no preference.
+ void setGameModeFrameRateForUid(FrameRateOverride) EXCLUDES(mDisplayLock);
+
+ // Stores the frame rate override that a game should run rat set by default game frame rate.
+ // FrameRateOverride.refreshRateHz == 0 means no preference, game default game frame rate is not
+ // enabled.
+ //
+ // "ro.surface_flinger.game_default_frame_rate_override" sets the frame rate value,
+ // "persist.graphics.game_default_frame_rate.enabled" controls whether this feature is enabled.
+ void setGameDefaultFrameRateForUid(FrameRateOverride) EXCLUDES(mDisplayLock);
void updateSmallAreaDetection(std::vector<std::pair<int32_t, float>>& uidThresholdMappings);
@@ -311,6 +337,9 @@
return pacesetterSelectorPtr()->getActiveMode().fps;
}
+ Fps getNextFrameInterval(PhysicalDisplayId, TimePoint currentExpectedPresentTime) const
+ EXCLUDES(mDisplayLock);
+
// Returns the framerate of the layer with the given sequence ID
float getLayerFramerate(nsecs_t now, int32_t id) const {
return mLayerHistory.getLayerFramerate(now, id);
@@ -318,9 +347,10 @@
bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
- // Returns true if the small dirty detection is enabled.
- bool supportSmallDirtyDetection() const {
- return mFeatures.test(Feature::kSmallDirtyContentDetection);
+ // Returns true if the small dirty detection is enabled for the appId.
+ bool supportSmallDirtyDetection(int32_t appId) {
+ return mFeatures.test(Feature::kSmallDirtyContentDetection) &&
+ mSmallAreaDetectionAllowMappings.getThresholdForAppId(appId).has_value();
}
// Injects a delay that is a fraction of the predicted frame duration for the next frame.
@@ -352,7 +382,7 @@
void onHardwareVsyncRequest(PhysicalDisplayId, bool enable);
void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable,
- std::optional<Fps> refreshRate = std::nullopt)
+ DisplayModePtr modePtr = nullptr)
REQUIRES(kMainThreadContext, mDisplayLock);
void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
@@ -446,9 +476,14 @@
const FeatureFlags mFeatures;
+ // Stores phase offsets configured per refresh rate.
+ const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration;
+
// Shifts the VSYNC phase during certain transactions and refresh rate changes.
const sp<VsyncModulator> mVsyncModulator;
+ const std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+
// Used to choose refresh rate if content detection is enabled.
LayerHistory mLayerHistory;
@@ -462,6 +497,8 @@
ISchedulerCallback& mSchedulerCallback;
+ IVsyncTrackerCallback& mVsyncTrackerCallback;
+
// mDisplayLock may be locked while under mPolicyLock.
mutable std::mutex mPolicyLock;
@@ -477,9 +514,7 @@
: displayId(displayId),
selectorPtr(std::move(selectorPtr)),
schedulePtr(std::move(schedulePtr)),
- targeterPtr(std::make_unique<
- FrameTargeter>(displayId,
- features.test(Feature::kBackpressureGpuComposition))) {}
+ targeterPtr(std::make_unique<FrameTargeter>(displayId, features)) {}
const PhysicalDisplayId displayId;
@@ -513,13 +548,17 @@
});
}
+ // The pacesetter must exist as a precondition.
+ ftl::NonNull<const Display*> pacesetterPtrLocked() const REQUIRES(mDisplayLock) {
+ return ftl::as_non_null(&pacesetterDisplayLocked()->get());
+ }
+
RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
std::scoped_lock lock(mDisplayLock);
return pacesetterSelectorPtrLocked();
}
RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
- ftl::FakeGuard guard(kMainThreadContext);
return pacesetterDisplayLocked()
.transform([](const Display& display) { return display.selectorPtr; })
.or_else([] { return std::optional<RefreshRateSelectorPtr>(nullptr); })
diff --git a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
index 38c6da4..7510ebf 100644
--- a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
+++ b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.cpp
@@ -29,7 +29,7 @@
}
}
-void SmallAreaDetectionAllowMappings::setThesholdForAppId(int32_t appId, float threshold) {
+void SmallAreaDetectionAllowMappings::setThresholdForAppId(int32_t appId, float threshold) {
if (!isValidThreshold(threshold)) return;
std::lock_guard lock(mLock);
diff --git a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
index e10301c..4ec5e3b 100644
--- a/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
+++ b/services/surfaceflinger/Scheduler/SmallAreaDetectionAllowMappings.h
@@ -28,7 +28,7 @@
public:
void update(std::vector<std::pair<int32_t, float>>& appIdThresholdMappings);
- void setThesholdForAppId(int32_t appId, float threshold) EXCLUDES(mLock);
+ void setThresholdForAppId(int32_t appId, float threshold) EXCLUDES(mLock);
std::optional<float> getThresholdForAppId(int32_t uid) EXCLUDES(mLock);
private:
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index c3a952f..f978016 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -84,8 +84,8 @@
* able to provide the ready-by time (deadline) on the callback.
* For internal clients, we don't need to add additional padding, so
* readyDuration will typically be 0.
- * @earliestVsync: The targeted display time. This will be snapped to the closest
- * predicted vsync time after earliestVsync.
+ * @lastVsync: The targeted display time. This will be snapped to the closest
+ * predicted vsync time after lastVsync.
*
* callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
@@ -93,11 +93,11 @@
struct ScheduleTiming {
nsecs_t workDuration = 0;
nsecs_t readyDuration = 0;
- nsecs_t earliestVsync = 0;
+ nsecs_t lastVsync = 0;
bool operator==(const ScheduleTiming& other) const {
return workDuration == other.workDuration && readyDuration == other.readyDuration &&
- earliestVsync == other.earliestVsync;
+ lastVsync == other.lastVsync;
}
bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
@@ -109,12 +109,12 @@
* The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
*
- * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+ * The caller designates the earliest vsync event that should be targeted by the lastVsync
* parameter.
* The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where
- * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ).
+ * predictedVsync is the first vsync event time where ( predictedVsync >= lastVsync ).
*
- * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has
+ * If (workDuration + readyDuration - lastVsync) is in the past, or if a callback has
* already been dispatched for the predictedVsync, an error will be returned.
*
* It is valid to reschedule a callback to a different time.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index f467670..5cb0ffb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -25,33 +25,32 @@
#include <scheduler/TimeKeeper.h>
+#include <common/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);
+ const auto nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
+ now + timing.workDuration +
+ timing.readyDuration),
+ timing.lastVsync);
+ return getExpectedCallbackTime(nextVsyncTime, timing);
}
} // namespace
@@ -97,31 +96,36 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
- std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+ auto nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
+ now + timing.workDuration +
+ timing.readyDuration),
+ timing.lastVsync);
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
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) {
@@ -141,11 +145,13 @@
bool const nextVsyncTooClose = mLastDispatchTime &&
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
if (alreadyDispatchedForVsync) {
- return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+ return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
+ *mLastDispatchTime);
}
if (nextVsyncTooClose) {
- return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod);
+ return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
+ *mLastDispatchTime + currentPeriod);
}
return nextVsyncTime;
@@ -162,11 +168,12 @@
}
const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
- const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
+ const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.lastVsync);
const auto nextVsyncTime =
adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
- tracker.nextAnticipatedVSyncTimeFrom(earliestVsync));
+ tracker.nextAnticipatedVSyncTimeFrom(earliestVsync,
+ mScheduleTiming.lastVsync));
const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
@@ -216,10 +223,10 @@
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
StringAppendF(&result,
- "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative "
"to now\n",
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
- (mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
+ (mScheduleTiming.lastVsync - systemTime()) / 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
@@ -239,6 +246,7 @@
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard lock(mMutex);
+ mRunning = false;
cancelTimer();
for (auto& [_, entry] : mCallbacks) {
ALOGE("Forgot to unregister a callback on VSyncDispatch!");
@@ -307,6 +315,10 @@
std::vector<Invocation> invocations;
{
std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGD("TimerQueue is not running. Skipping callback.");
+ return;
+ }
auto const now = mTimeKeeper->now();
mLastTimerCallback = now;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index e0fb8f9..3d08410 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -148,6 +148,10 @@
std::mutex mutable mMutex;
+ // During VSyncDispatchTimerQueue deconstruction, skip timerCallback to
+ // avoid crash
+ bool mRunning = true;
+
static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
std::unique_ptr<TimeKeeper> const mTimeKeeper;
VsyncSchedule::TrackerPtr mTracker;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e969fdc..28e35de 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -29,6 +29,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <ftl/concat.h>
@@ -46,14 +47,16 @@
VSyncPredictor::~VSyncPredictor() = default;
-VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize,
- size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
- : mId(id),
+VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
+ IVsyncTrackerCallback& callback)
+ : mId(modePtr->getPhysicalDisplayId()),
mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
- mIdealPeriod(idealPeriod) {
+ mVsyncTrackerCallback(callback),
+ mDisplayModePtr(modePtr) {
resetModel();
}
@@ -71,15 +74,21 @@
return (i + 1) % mTimestamps.size();
}
+nsecs_t VSyncPredictor::idealPeriod() const {
+ return mDisplayModePtr->getVsyncRate().getPeriodNsecs();
+}
+
bool VSyncPredictor::validate(nsecs_t timestamp) const {
if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
return true;
}
- auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
- auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
+ const auto aValidTimestamp = mTimestamps[mLastTimestampIndex];
+ const auto percent =
+ (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent &&
percent <= (kMaxPercent - kOutlierTolerancePercent)) {
+ ATRACE_FORMAT_INSTANT("timestamp is not aligned with model");
return false;
}
@@ -87,9 +96,10 @@
[timestamp](nsecs_t a, nsecs_t b) {
return std::abs(timestamp - a) < std::abs(timestamp - b);
});
- const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / mIdealPeriod;
+ const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod();
if (distancePercent < kOutlierTolerancePercent) {
// duplicate timestamp
+ ATRACE_FORMAT_INSTANT("duplicate timestamp");
return false;
}
return true;
@@ -97,10 +107,29 @@
nsecs_t VSyncPredictor::currentPeriod() const {
std::lock_guard lock(mMutex);
- return mRateMap.find(mIdealPeriod)->second.slope;
+ return mRateMap.find(idealPeriod())->second.slope;
+}
+
+Period VSyncPredictor::minFramePeriod() const {
+ if (!FlagManager::getInstance().vrr_config()) {
+ return Period::fromNs(currentPeriod());
+ }
+
+ std::lock_guard lock(mMutex);
+ return minFramePeriodLocked();
+}
+
+Period VSyncPredictor::minFramePeriodLocked() const {
+ const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs();
+ const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
+ static_cast<float>(idealPeriod())));
+ const auto slope = mRateMap.find(idealPeriod())->second.slope;
+ return Period::fromNs(slope * numPeriods);
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+ ATRACE_CALL();
+
std::lock_guard lock(mMutex);
if (!validate(timestamp)) {
@@ -119,6 +148,8 @@
} else {
mKnownTimestamp = timestamp;
}
+ ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
+ (systemTime() - *mKnownTimestamp) / 1e6f);
return false;
}
@@ -134,7 +165,7 @@
const size_t numSamples = mTimestamps.size();
if (numSamples < kMinimumSamplesForPrediction) {
- mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+ mRateMap[idealPeriod()] = {idealPeriod(), 0};
return true;
}
@@ -158,7 +189,7 @@
// Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
- auto it = mRateMap.find(mIdealPeriod);
+ auto it = mRateMap.find(idealPeriod());
auto const currentPeriod = it->second.slope;
// The mean of the ordinals must be precise for the intercept calculation, so scale them up for
@@ -196,7 +227,7 @@
}
if (CC_UNLIKELY(bottom == 0)) {
- it->second = {mIdealPeriod, 0};
+ it->second = {idealPeriod(), 0};
clearTimestamps();
return false;
}
@@ -204,9 +235,9 @@
nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
- auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
+ auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent) {
- it->second = {mIdealPeriod, 0};
+ it->second = {idealPeriod(), 0};
clearTimestamps();
return false;
}
@@ -222,7 +253,7 @@
}
auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
- const auto vsync = nextAnticipatedVSyncTimeFromLocked(timestamp);
+ const auto vsync = snapToVsync(timestamp);
if (!mLastVsyncSequence) return {vsync, 0};
const auto [slope, _] = getVSyncPredictionModelLocked();
@@ -232,14 +263,14 @@
return {vsync, vsyncSequence};
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
traceInt64("VSP-mode", 1);
auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
- auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
- return knownTimestamp + numPeriodsOut * mIdealPeriod;
+ auto const numPeriodsOut = ((timePoint - knownTimestamp) / idealPeriod()) + 1;
+ return knownTimestamp + numPeriodsOut * idealPeriod();
}
auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
@@ -268,23 +299,48 @@
return prediction;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt) const {
+ ATRACE_CALL();
std::lock_guard lock(mMutex);
+ const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
+ const auto threshold = currentPeriod / 2;
+ const auto minFramePeriod = minFramePeriodLocked().ns();
+ const auto lastFrameMissed =
+ lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold;
+ const nsecs_t baseTime =
+ FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt
+ ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold)
+ : timePoint;
+ const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime);
+ if (FlagManager::getInstance().vrr_config()) {
+ const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
+ const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
+ mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
+ }
+ return vsyncTime;
+}
+nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const {
// update the mLastVsyncSequence for reference point
mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
- if (!mRenderRate) return 0;
-
+ if (!mRenderRateOpt) return 0;
const auto divisor =
- RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod),
- *mRenderRate);
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
+ *mRenderRateOpt);
if (divisor <= 1) return 0;
- const int mod = mLastVsyncSequence->seq % divisor;
+ int mod = mLastVsyncSequence->seq % divisor;
if (mod == 0) return 0;
+ // This is actually a bug fix, but guarded with vrr_config since we found it with this
+ // config
+ if (FlagManager::getInstance().vrr_config()) {
+ if (mod < 0) mod += divisor;
+ }
+
return divisor - mod;
}();
@@ -294,7 +350,7 @@
auto const [slope, intercept] = getVSyncPredictionModelLocked();
const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
- return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
+ return snapToVsync(approximateNextVsync - slope / 2);
}
/*
@@ -308,7 +364,8 @@
bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
std::lock_guard lock(mMutex);
const auto divisor =
- RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
+ frameRate);
return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
}
@@ -324,7 +381,7 @@
return true;
}
- const nsecs_t period = mRateMap[mIdealPeriod].slope;
+ const nsecs_t period = mRateMap[idealPeriod()].slope;
const nsecs_t justBeforeTimePoint = timePoint - period / 2;
const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint);
ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64,
@@ -332,10 +389,123 @@
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::setRenderRate(Fps renderRate) {
+ ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
+ ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
std::lock_guard lock(mMutex);
- mRenderRate = fps;
+ mRenderRateOpt = renderRate;
+}
+
+void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
+ LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(),
+ "mode does not belong to the display");
+ ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str());
+ const auto timeout = modePtr->getVrrConfig()
+ ? modePtr->getVrrConfig()->notifyExpectedPresentConfig
+ : std::nullopt;
+ ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
+ to_string(*modePtr).c_str(),
+ timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
+ std::lock_guard lock(mMutex);
+
+ mDisplayModePtr = modePtr;
+ traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
+
+ static constexpr size_t kSizeLimit = 30;
+ if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
+ mRateMap.erase(mRateMap.begin());
+ }
+
+ if (mRateMap.find(idealPeriod()) == mRateMap.end()) {
+ mRateMap[idealPeriod()] = {idealPeriod(), 0};
+ }
+
+ clearTimestamps();
+}
+
+void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
+ TimePoint lastConfirmedPresentTime) {
+ const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
+ const auto threshold = currentPeriod / 2;
+ const auto minFramePeriod = minFramePeriodLocked().ns();
+
+ auto prev = lastConfirmedPresentTime.ns();
+ for (auto& current : mPastExpectedPresentTimes) {
+ if (CC_UNLIKELY(mTraceOn)) {
+ ATRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
+ static_cast<float>(current.ns() - lastConfirmedPresentTime.ns()) /
+ 1e6f);
+ }
+
+ const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod;
+ if (minPeriodViolation) {
+ ATRACE_NAME("minPeriodViolation");
+ current = TimePoint::fromNs(prev + minFramePeriod);
+ prev = current.ns();
+ } else {
+ break;
+ }
+ }
+
+ if (!mPastExpectedPresentTimes.empty()) {
+ const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
+ if (phase > 0ns) {
+ if (mLastVsyncSequence) {
+ mLastVsyncSequence->vsyncTime += phase.ns();
+ }
+ mPastExpectedPresentTimes.clear();
+ }
+ }
+}
+
+void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
+ TimePoint lastConfirmedPresentTime) {
+ ATRACE_CALL();
+ std::lock_guard lock(mMutex);
+
+ if (!mDisplayModePtr->getVrrConfig()) return;
+
+ if (CC_UNLIKELY(mTraceOn)) {
+ ATRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence",
+ static_cast<float>(expectedPresentTime.ns() -
+ lastConfirmedPresentTime.ns()) /
+ 1e6f);
+ }
+ const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
+ const auto threshold = currentPeriod / 2;
+ mPastExpectedPresentTimes.push_back(expectedPresentTime);
+
+ while (!mPastExpectedPresentTimes.empty()) {
+ const auto front = mPastExpectedPresentTimes.front().ns();
+ const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
+ if (frontIsBeforeConfirmed) {
+ if (CC_UNLIKELY(mTraceOn)) {
+ ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
+ static_cast<float>(lastConfirmedPresentTime.ns() - front) /
+ 1e6f);
+ }
+ mPastExpectedPresentTimes.pop_front();
+ } else {
+ break;
+ }
+ }
+
+ ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+}
+
+void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
+ ATRACE_CALL();
+
+ std::lock_guard lock(mMutex);
+ if (!mDisplayModePtr->getVrrConfig()) return;
+
+ // We don't know when the frame is going to be presented, so we assume it missed one vsync
+ const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
+ const auto lastConfirmedPresentTime =
+ TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);
+
+ ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ mLastMissedVsync = expectedPresentTime;
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -345,28 +515,12 @@
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
- return mRateMap.find(mIdealPeriod)->second;
-}
-
-void VSyncPredictor::setPeriod(nsecs_t period) {
- ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str());
- traceInt64("VSP-setPeriod", period);
-
- std::lock_guard lock(mMutex);
- static constexpr size_t kSizeLimit = 30;
- if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
- mRateMap.erase(mRateMap.begin());
- }
-
- mIdealPeriod = period;
- if (mRateMap.find(period) == mRateMap.end()) {
- mRateMap[mIdealPeriod] = {period, 0};
- }
-
- clearTimestamps();
+ return mRateMap.find(idealPeriod())->second;
}
void VSyncPredictor::clearTimestamps() {
+ ATRACE_CALL();
+
if (!mTimestamps.empty()) {
auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
if (mKnownTimestamp) {
@@ -387,18 +541,18 @@
void VSyncPredictor::resetModel() {
std::lock_guard lock(mMutex);
- mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+ mRateMap[idealPeriod()] = {idealPeriod(), 0};
clearTimestamps();
}
void VSyncPredictor::dump(std::string& result) const {
std::lock_guard lock(mMutex);
- StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+ StringAppendF(&result, "\tmDisplayModePtr=%s\n", to_string(*mDisplayModePtr).c_str());
StringAppendF(&result, "\tRefresh Rate Map:\n");
- for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+ for (const auto& [period, periodInterceptTuple] : mRateMap) {
StringAppendF(&result,
"\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
- idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+ period / 1e6f, periodInterceptTuple.slope / 1e6f,
periodInterceptTuple.intercept);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index c01c44d..9191003 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -16,6 +16,7 @@
#pragma once
+#include <deque>
#include <mutex>
#include <unordered_map>
#include <vector>
@@ -31,30 +32,26 @@
public:
/*
* \param [in] PhysicalDisplayid The display this corresponds to.
- * \param [in] idealPeriod The initial ideal period to use.
+ * \param [in] modePtr The initial display mode
* \param [in] historySize The internal amount of entries to store in the model.
* \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);
+ VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
+ size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
+ IVsyncTrackerCallback&);
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt = {}) const final
+ EXCLUDES(mMutex);
nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+ Period minFramePeriod() const final EXCLUDES(mMutex);
void resetModel() final EXCLUDES(mMutex);
- /*
- * Inform the model that the period is anticipated to change to a new value.
- * model will use the period parameter to predict vsync events until enough
- * timestamps with the new period have been collected.
- *
- * \param [in] period The new period that should be used.
- */
- void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
-
/* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
@@ -69,8 +66,14 @@
bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
+ void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
+
void setRenderRate(Fps) final EXCLUDES(mMutex);
+ void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
+ EXCLUDES(mMutex);
+ void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex);
+
void dump(std::string& result) const final EXCLUDES(mMutex);
private:
@@ -86,22 +89,26 @@
size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
- nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
+ nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex);
+ nsecs_t snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const REQUIRES(mMutex);
bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
+ Period minFramePeriodLocked() const REQUIRES(mMutex);
+ void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
struct VsyncSequence {
nsecs_t vsyncTime;
int64_t seq;
};
VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex);
+ nsecs_t idealPeriod() const REQUIRES(mMutex);
bool const mTraceOn;
size_t const kHistorySize;
size_t const kMinimumSamplesForPrediction;
size_t const kOutlierTolerancePercent;
+ IVsyncTrackerCallback& mVsyncTrackerCallback;
std::mutex mutable mMutex;
- nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
// Map between ideal vsync period and the calculated model
@@ -110,9 +117,14 @@
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
- std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
+ ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex);
+ std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);
mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
+
+ std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
+
+ TimePoint mLastMissedVsync GUARDED_BY(mMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 2938aa3..186a2d6 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -53,6 +53,8 @@
VSyncReactor::~VSyncReactor() = default;
bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
+ ATRACE_CALL();
+
if (!fence) {
return false;
}
@@ -64,6 +66,8 @@
std::lock_guard lock(mMutex);
if (mExternalIgnoreFences || mInternalIgnoreFences) {
+ ATRACE_FORMAT_INSTANT("mExternalIgnoreFences=%d mInternalIgnoreFences=%d",
+ mExternalIgnoreFences, mInternalIgnoreFences);
return true;
}
@@ -116,32 +120,34 @@
}
}
-void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
+void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> modePtr) {
ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
mPeriodConfirmationInProgress = true;
- mPeriodTransitioningTo = newPeriod;
+ mModePtrTransitioningTo = modePtr.get();
mMoreSamplesNeeded = true;
setIgnorePresentFencesInternal(true);
}
void VSyncReactor::endPeriodTransition() {
ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
- mPeriodTransitioningTo.reset();
+ mModePtrTransitioningTo.reset();
mPeriodConfirmationInProgress = false;
mLastHwVsync.reset();
}
-void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) {
- ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period);
+void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) {
+ ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(),
+ modePtr->getVsyncRate().getPeriodNsecs());
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) {
+ if (!mSupportKernelIdleTimer &&
+ modePtr->getVsyncRate().getPeriodNsecs() == mTracker.currentPeriod() && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
} else {
- startPeriodTransitionInternal(period);
+ startPeriodTransitionInternal(modePtr);
}
}
@@ -159,14 +165,16 @@
return false;
}
- const bool periodIsChanging =
- mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod());
+ const std::optional<Period> newPeriod = mModePtrTransitioningTo
+ ? mModePtrTransitioningTo->getVsyncRate().getPeriod()
+ : std::optional<Period>{};
+ const bool periodIsChanging = newPeriod && (newPeriod->ns() != mTracker.currentPeriod());
if (mSupportKernelIdleTimer && !periodIsChanging) {
// Clear out the Composer-provided period and use the allowance logic below
HwcVsyncPeriod = {};
}
- auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod();
+ auto const period = newPeriod ? newPeriod->ns() : mTracker.currentPeriod();
static constexpr int allowancePercent = 10;
static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -185,8 +193,8 @@
std::lock_guard lock(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value);
- if (mPeriodTransitioningTo) {
- mTracker.setPeriod(*mPeriodTransitioningTo);
+ if (mModePtrTransitioningTo) {
+ mTracker.setDisplayModePtr(ftl::as_non_null(mModePtrTransitioningTo));
*periodFlushed = true;
}
@@ -228,10 +236,11 @@
mInternalIgnoreFences, mExternalIgnoreFences);
StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
mMoreSamplesNeeded, mPeriodConfirmationInProgress);
- if (mPeriodTransitioningTo) {
- StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+ if (mModePtrTransitioningTo) {
+ StringAppendF(&result, "mModePtrTransitioningTo=%s\n",
+ to_string(*mModePtrTransitioningTo).c_str());
} else {
- StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+ StringAppendF(&result, "mModePtrTransitioningTo=nullptr\n");
}
if (mLastHwVsync) {
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index f230242..2415a66 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -27,6 +27,7 @@
#include <scheduler/TimeKeeper.h>
+#include "VSyncTracker.h"
#include "VsyncController.h"
namespace android::scheduler {
@@ -45,7 +46,7 @@
bool addPresentFence(std::shared_ptr<FenceTime>) final;
void setIgnorePresentFences(bool ignore) final;
- void startPeriodTransition(nsecs_t period, bool force) final;
+ void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) final;
bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) final;
@@ -57,7 +58,7 @@
private:
void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex);
void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
- void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex);
+ void startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr>) REQUIRES(mMutex);
void endPeriodTransition() REQUIRES(mMutex);
bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
REQUIRES(mMutex);
@@ -74,7 +75,7 @@
bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
- std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
+ DisplayModePtr mModePtrTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON;
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index bc0e3bc..417163f 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -16,13 +16,22 @@
#pragma once
+#include <ui/DisplayId.h>
#include <utils/Timers.h>
#include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
#include "VSyncDispatch.h"
namespace android::scheduler {
+
+struct IVsyncTrackerCallback {
+ virtual ~IVsyncTrackerCallback() = default;
+ virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
+ Fps renderRate) = 0;
+};
+
/*
* VSyncTracker is an interface for providing estimates on future Vsync signal times based on
* historical vsync timing data.
@@ -48,9 +57,13 @@
* is updated.
*
* \param [in] timePoint The point in time after which to estimate a vsync event.
+ * \param [in] lastVsyncOpt The last vsync time used by the client. If provided, the tracker
+ * should use that as a reference point when generating the new vsync
+ * and avoid crossing the minimal frame period of a VRR display.
* \return A prediction of the timestamp of a vsync event.
*/
- virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+ virtual nsecs_t nextAnticipatedVSyncTimeFrom(
+ nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0;
/*
* The current period of the vsync signal.
@@ -60,11 +73,9 @@
virtual nsecs_t currentPeriod() const = 0;
/*
- * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
- *
- * \param [in] period The period that the system is changing into.
+ * The minimal period frames can be displayed.
*/
- virtual void setPeriod(nsecs_t period) = 0;
+ virtual Period minFramePeriod() const = 0;
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
@@ -80,6 +91,15 @@
virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
/*
+ * Sets the active mode of the display which includes the vsync period and other VRR attributes.
+ * This will inform the tracker that the period is changing and the tracker needs to recalibrate
+ * itself.
+ *
+ * \param [in] DisplayModePtr The display mode the tracker will use.
+ */
+ virtual void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) = 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.
* The tracker will continue to track the vsync timeline and expect it
@@ -91,6 +111,11 @@
*/
virtual void setRenderRate(Fps) = 0;
+ virtual void onFrameBegin(TimePoint expectedPresentTime,
+ TimePoint lastConfirmedPresentTime) = 0;
+
+ virtual void onFrameMissed(TimePoint expectedPresentTime) = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 9177899..807a7fb 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -22,6 +22,7 @@
#include <DisplayHardware/HWComposer.h>
#include <DisplayHardware/Hal.h>
+#include <scheduler/FrameRateMode.h>
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -59,13 +60,14 @@
bool* periodFlushed) = 0;
/*
- * Inform the controller that the period is changing and the controller needs to recalibrate
- * itself. The controller will end the period transition internally.
+ * Inform the controller that the display mode is changing and the controller needs to
+ * recalibrate itself to the new vsync period. The controller will end the period transition
+ * internally.
*
- * \param [in] period The period that the system is changing into.
+ * \param [in] DisplayModePtr The new mode the display is changing to.
* \param [in] force True to recalibrate even if period matches the existing period.
*/
- virtual void startPeriodTransition(nsecs_t period, bool force) = 0;
+ virtual void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) = 0;
/*
* Tells the tracker to stop using present fences to get a vsync signal.
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index ff3f29d..db6a187 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -16,7 +16,10 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <common/FlagManager.h>
+
#include <ftl/fake_guard.h>
+#include <gui/TraceUtils.h>
#include <scheduler/Fps.h>
#include <scheduler/Timer.h>
@@ -53,13 +56,14 @@
VSyncCallbackRegistration mRegistration;
};
-VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features,
- RequestHardwareVsync requestHardwareVsync)
- : mId(id),
+VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features,
+ RequestHardwareVsync requestHardwareVsync,
+ IVsyncTrackerCallback& callback)
+ : mId(modePtr->getPhysicalDisplayId()),
mRequestHardwareVsync(std::move(requestHardwareVsync)),
- mTracker(createTracker(id)),
+ mTracker(createTracker(modePtr, callback)),
mDispatch(createDispatch(mTracker)),
- mController(createController(id, *mTracker, features)),
+ mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)),
mTracer(features.test(Feature::kTracePredictedVsync)
? std::make_unique<PredictedVsyncTracer>(mDispatch)
: nullptr) {}
@@ -78,6 +82,13 @@
return Period::fromNs(mTracker->currentPeriod());
}
+Period VsyncSchedule::minFramePeriod() const {
+ if (FlagManager::getInstance().vrr_config()) {
+ return mTracker->minFramePeriod();
+ }
+ return period();
+}
+
TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const {
return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns()));
}
@@ -100,15 +111,15 @@
mDispatch->dump(out);
}
-VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) {
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr,
+ IVsyncTrackerCallback& callback) {
// TODO(b/144707443): Tune constants.
- constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
constexpr size_t kHistorySize = 20;
constexpr size_t kMinSamplesForPrediction = 6;
constexpr uint32_t kDiscardOutlierPercent = 20;
- return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize,
- kMinSamplesForPrediction, kDiscardOutlierPercent);
+ return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction,
+ kDiscardOutlierPercent, callback);
}
VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
@@ -137,9 +148,9 @@
return reactor;
}
-void VsyncSchedule::startPeriodTransition(Period period, bool force) {
+void VsyncSchedule::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) {
std::lock_guard<std::mutex> lock(mHwVsyncLock);
- mController->startPeriodTransition(period.ns(), force);
+ mController->onDisplayModeChanged(modePtr, force);
enableHardwareVsyncLocked();
}
@@ -169,6 +180,7 @@
}
void VsyncSchedule::enableHardwareVsyncLocked() {
+ ATRACE_CALL();
if (mHwVsyncState == HwVsyncState::Disabled) {
getTracker().resetModel();
mRequestHardwareVsync(mId, true);
@@ -177,6 +189,7 @@
}
void VsyncSchedule::disableHardwareVsync(bool disallow) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> lock(mHwVsyncLock);
switch (mHwVsyncState) {
case HwVsyncState::Enabled:
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 47e92e1..722ea0b 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,21 +57,23 @@
public:
using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
- VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
+ VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync,
+ IVsyncTrackerCallback&);
~VsyncSchedule();
// IVsyncSource overrides:
Period period() const override;
TimePoint vsyncDeadlineAfter(TimePoint) const override;
+ Period minFramePeriod() const override;
- // Inform the schedule that the period is changing and the schedule needs to recalibrate
- // itself. The schedule will end the period transition internally. This will
- // enable hardware VSYNCs in order to calibrate.
+ // Inform the schedule that the display mode changed the schedule needs to recalibrate
+ // itself to the new vsync period. The schedule will end the period transition internally.
+ // This will enable hardware VSYNCs in order to calibrate.
//
- // \param [in] period The period that the system is changing into.
+ // \param [in] DisplayModePtr The mode that the display is changing to.
// \param [in] force True to force a transition even if it is not a
// change.
- void startPeriodTransition(Period period, bool force);
+ void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force);
// Pass a VSYNC sample to VsyncController. Return true if
// VsyncController detected that the VSYNC period changed. Enable or disable
@@ -124,7 +127,7 @@
friend class android::VsyncScheduleTest;
friend class android::fuzz::SchedulerFuzzer;
- static TrackerPtr createTracker(PhysicalDisplayId);
+ static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&);
static DispatchPtr createDispatch(TrackerPtr);
static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
index 7c72ac6..52485fb 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -29,6 +29,7 @@
kTracePredictedVsync = 1 << 3,
kBackpressureGpuComposition = 1 << 4,
kSmallDirtyContentDetection = 1 << 5,
+ kExpectedPresentTime = 1 << 6,
};
using FeatureFlags = ftl::Flags<Feature>;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 19e951a..84ef89f 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -83,11 +83,12 @@
};
// The frame rate category of a Layer.
-enum class FrameRateCategory {
+enum class FrameRateCategory : int32_t {
Default,
NoPreference,
Low,
Normal,
+ HighHint,
High,
ftl_last = High
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index ae74205..a5bb6c2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -19,11 +19,13 @@
#include <array>
#include <atomic>
#include <memory>
+#include <optional>
#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
+#include <scheduler/Features.h>
#include <scheduler/Time.h>
#include <scheduler/VsyncId.h>
#include <scheduler/interface/CompositeResult.h>
@@ -49,28 +51,23 @@
TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
- // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
- TimePoint pastVsyncTime(Period vsyncPeriod) const;
+ std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
- // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
- TimePoint previousFrameVsyncTime(Period vsyncPeriod) const {
- return mExpectedPresentTime - vsyncPeriod;
- }
+ // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
+ TimePoint pastVsyncTime(Period minFramePeriod) const;
// The present fence for the frame that had targeted the most recent VSYNC before this frame.
// If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
// VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
// `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
// signaled by now (unless that frame missed).
- const FenceTimePtr& presentFenceForPastVsync(Period vsyncPeriod) const;
+ const FenceTimePtr& presentFenceForPastVsync(Period minFramePeriod) const;
// Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
const FenceTimePtr& presentFenceForPreviousFrame() const {
return mPresentFences.front().fenceTime;
}
- bool wouldPresentEarly(Period vsyncPeriod) const;
-
bool isFramePending() const { return mFramePending; }
bool didMissFrame() const { return mFrameMissed; }
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
@@ -79,9 +76,17 @@
explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
+ bool wouldPresentEarly(Period minFramePeriod) const;
+
+ // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
+ TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
+ return mExpectedPresentTime - minFramePeriod;
+ }
+
VsyncId mVsyncId;
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
+ std::optional<TimePoint> mEarliestPresentTime;
TracedOrdinal<bool> mFramePending;
TracedOrdinal<bool> mFrameMissed;
@@ -95,19 +100,22 @@
std::array<FenceWithFenceTime, 2> mPresentFences;
private:
+ friend class FrameTargeterTestBase;
+
template <int N>
- inline bool targetsVsyncsAhead(Period vsyncPeriod) const {
+ inline bool targetsVsyncsAhead(Period minFramePeriod) const {
static_assert(N > 1);
- return expectedFrameDuration() > (N - 1) * vsyncPeriod;
+ return expectedFrameDuration() > (N - 1) * minFramePeriod;
}
};
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
class FrameTargeter final : private FrameTarget {
public:
- FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition)
+ FrameTargeter(PhysicalDisplayId displayId, FeatureFlags flags)
: FrameTarget(to_string(displayId)),
- mBackpressureGpuComposition(backpressureGpuComposition) {}
+ mBackpressureGpuComposition(flags.test(Feature::kBackpressureGpuComposition)),
+ mSupportsExpectedPresentTime(flags.test(Feature::kExpectedPresentTime)) {}
const FrameTarget& target() const { return *this; }
@@ -116,10 +124,14 @@
VsyncId vsyncId;
TimePoint expectedVsyncTime;
Duration sfWorkDuration;
+ Duration hwcMinWorkDuration;
};
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
+ std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod,
+ Duration hwcMinWorkDuration);
+
// TODO(b/241285191): Merge with FrameTargeter::endFrame.
FenceTimePtr setPresentFence(sp<Fence>);
@@ -128,7 +140,7 @@
void dump(utils::Dumper&) const;
private:
- friend class FrameTargeterTest;
+ friend class FrameTargeterTestBase;
// For tests.
using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs);
@@ -138,6 +150,7 @@
static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
const bool mBackpressureGpuComposition;
+ const bool mSupportsExpectedPresentTime;
TimePoint mScheduledPresentTime;
CompositionCoverageFlags mCompositionCoverage;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
index bb2de75..0154060 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
@@ -23,6 +23,7 @@
struct IVsyncSource {
virtual Period period() const = 0;
virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0;
+ virtual Period minFramePeriod() const = 0;
protected:
~IVsyncSource() = default;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 7a18654..68c277d 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -27,28 +27,28 @@
mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
-TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
+TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const {
// TODO(b/267315508): Generalize to N VSYNCs.
- const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
- return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift);
+ const int shift = static_cast<int>(targetsVsyncsAhead<2>(minFramePeriod));
+ return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
}
-const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const {
+const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
// TODO(b/267315508): Generalize to N VSYNCs.
- const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod));
+ const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
return mPresentFences[i].fenceTime;
}
-bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const {
+bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
// TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
// should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
// TODO(b/267315508): Generalize to N VSYNCs.
- if (targetsVsyncsAhead<3>(vsyncPeriod)) {
+ if (targetsVsyncsAhead<3>(minFramePeriod)) {
return true;
}
- const auto fence = presentFenceForPastVsync(vsyncPeriod);
+ const auto fence = presentFenceForPastVsync(minFramePeriod);
return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
}
@@ -68,6 +68,7 @@
mScheduledPresentTime = args.expectedVsyncTime;
const Period vsyncPeriod = vsyncSource.period();
+ const Period minFramePeriod = vsyncSource.minFramePeriod();
// Calculate the expected present time once and use the cached value throughout this frame to
// make sure all layers are seeing this same value.
@@ -81,11 +82,15 @@
}
}
+ if (!mSupportsExpectedPresentTime) {
+ mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration);
+ }
+
ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
- const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod);
+ const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod);
// In cases where the present fence is about to fire, give it a small grace period instead of
// giving up on the frame.
@@ -120,6 +125,14 @@
if (mGpuFrameMissed) mGpuFrameMissedCount++;
}
+std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod,
+ Duration hwcMinWorkDuration) {
+ if (wouldPresentEarly(minFramePeriod)) {
+ return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
+ }
+ return {};
+}
+
void FrameTargeter::endFrame(const CompositeResult& result) {
mCompositionCoverage = result.compositionCoverage;
}
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 1e038d1..a9abcaf 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -17,48 +17,64 @@
#include <ftl/optional.h>
#include <gtest/gtest.h>
+#include <common/test/FlagUtils.h>
#include <scheduler/Fps.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/IVsyncSource.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
+
using namespace std::chrono_literals;
namespace android::scheduler {
namespace {
struct VsyncSource final : IVsyncSource {
- VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {}
+ VsyncSource(Period period, Period minFramePeriod, TimePoint deadline)
+ : vsyncPeriod(period), framePeriod(minFramePeriod), vsyncDeadline(deadline) {}
const Period vsyncPeriod;
+ const Period framePeriod;
const TimePoint vsyncDeadline;
Period period() const override { return vsyncPeriod; }
TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; }
+ Period minFramePeriod() const override { return framePeriod; }
};
} // namespace
-class FrameTargeterTest : public testing::Test {
+class FrameTargeterTestBase : public testing::Test {
public:
+ FrameTargeterTestBase(FeatureFlags flags) : mTargeter(PhysicalDisplayId::fromPort(13), flags) {}
+
const auto& target() const { return mTargeter.target(); }
+ bool wouldPresentEarly(Period minFramePeriod) const {
+ return target().wouldPresentEarly(minFramePeriod);
+ }
+
struct Frame {
- Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
- Duration frameDuration, Fps refreshRate,
+ Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
+ Duration frameDuration, Fps refreshRate, Fps peakRefreshRate,
FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
- : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) {
+ : testPtr(testPtr),
+ frameBeginTime(frameBeginTime),
+ period(refreshRate.getPeriod()),
+ minFramePeriod(peakRefreshRate.getPeriod()) {
const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime,
.vsyncId = vsyncId,
.expectedVsyncTime =
frameBeginTime + frameDuration,
- .sfWorkDuration = 10ms};
+ .sfWorkDuration = 10ms,
+ .hwcMinWorkDuration = kHwcMinWorkDuration};
testPtr->mTargeter.beginFrame(args,
vsyncSourceOpt
.or_else([&] {
return std::make_optional(
- VsyncSource(period,
+ VsyncSource(period, period,
args.expectedVsyncTime));
})
.value(),
@@ -84,26 +100,40 @@
static bool fencePending(const FenceTimePtr&, int) { return true; }
static bool fenceSignaled(const FenceTimePtr&, int) { return false; }
- FrameTargeterTest* const testPtr;
+ FrameTargeterTestBase* const testPtr;
TimePoint& frameBeginTime;
const Period period;
+ const Period minFramePeriod;
bool ended = false;
};
+ static constexpr Duration kHwcMinWorkDuration = std::chrono::nanoseconds(5ns);
+
private:
FenceToFenceTimeMap mFenceMap;
- static constexpr bool kBackpressureGpuComposition = true;
- FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition};
+ FrameTargeter mTargeter;
+};
+
+class FrameTargeterTest : public FrameTargeterTestBase {
+public:
+ FrameTargeterTest() : FrameTargeterTestBase(Feature::kBackpressureGpuComposition) {}
+};
+
+class FrameTargeterWithExpectedPresentSupportTest : public FrameTargeterTestBase {
+public:
+ FrameTargeterWithExpectedPresentSupportTest()
+ : FrameTargeterTestBase(FeatureFlags(Feature::kBackpressureGpuComposition) |
+ Feature::kExpectedPresentTime) {}
};
TEST_F(FrameTargeterTest, targetsFrames) {
VsyncId vsyncId{42};
{
TimePoint frameBeginTime(989ms);
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz);
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz, 60_Hz);
EXPECT_EQ(target().vsyncId(), VsyncId{42});
EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms));
@@ -112,7 +142,7 @@
}
{
TimePoint frameBeginTime(1100ms);
- const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz);
+ const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz, 60_Hz);
EXPECT_EQ(target().vsyncId(), VsyncId{43});
EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms));
@@ -127,9 +157,10 @@
TimePoint frameBeginTime(777ms);
constexpr Fps kRefreshRate = 120_Hz;
- const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms);
+ const VsyncSource vsyncSource(kRefreshRate.getPeriod(), kRefreshRate.getPeriod(),
+ frameBeginTime + 5ms);
const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate,
- Frame::fenceSignaled, vsyncSource);
+ kRefreshRate, Frame::fenceSignaled, vsyncSource);
EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod);
}
@@ -142,7 +173,7 @@
constexpr Duration kFrameDuration = 13ms;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
const auto fence = frame.end();
EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
@@ -160,7 +191,31 @@
FenceTimePtr previousFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
+ EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
+
+ previousFence = fence;
+ }
+}
+
+TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
+
+ VsyncId vsyncId{222};
+ TimePoint frameBeginTime(2000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Fps kPeakRefreshRate = 240_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 10ms;
+
+ FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
+ kPeakRefreshRate);
const auto fence = frame.end();
EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
@@ -173,7 +228,7 @@
TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
constexpr Period kPeriod = (60_Hz).getPeriod();
EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
}
TEST_F(FrameTargeterTest, detectsEarlyPresent) {
@@ -184,16 +239,51 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
const auto fence = frame.end();
fence->signalForTest(frameBeginTime.ns());
- EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, so it has an earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
+}
+
+// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
+// when there is expected present time support.
+TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
+ }
+
+ // The target is early if the past present fence was signaled.
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, but we have expected present time support, so it has no
+ // earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ ASSERT_EQ(std::nullopt, target().earliestPresentTime());
}
TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
@@ -204,21 +294,28 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
}
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
const auto fence = frame.end();
fence->signalForTest(frameBeginTime.ns());
// The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
- { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); }
+ { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
+
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// The target is early if the past present fence was signaled.
- EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
@@ -226,10 +323,13 @@
constexpr Fps kRefreshRate = 144_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate);
+ const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
// The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
- EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
TEST_F(FrameTargeterTest, detectsMissedFrames) {
@@ -243,7 +343,7 @@
EXPECT_FALSE(target().didMissHwcFrame());
{
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
EXPECT_FALSE(target().isFramePending());
// The frame did not miss if the past present fence is invalid.
@@ -251,7 +351,8 @@
EXPECT_FALSE(target().didMissHwcFrame());
}
{
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate,
+ Frame::fencePending);
EXPECT_TRUE(target().isFramePending());
// The frame missed if the past present fence is pending.
@@ -261,7 +362,8 @@
frame.end(CompositionCoverage::Gpu);
}
{
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate,
+ Frame::fencePending);
EXPECT_TRUE(target().isFramePending());
// The GPU frame missed if the past present fence is pending.
@@ -269,7 +371,7 @@
EXPECT_FALSE(target().didMissHwcFrame());
}
{
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
EXPECT_FALSE(target().isFramePending());
const auto fence = frame.end();
@@ -277,7 +379,7 @@
fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1);
}
{
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
EXPECT_FALSE(target().isFramePending());
const auto fence = frame.end();
@@ -289,7 +391,7 @@
EXPECT_TRUE(target().didMissHwcFrame());
}
{
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
EXPECT_FALSE(target().isFramePending());
// The frame did not miss if the past present fence was signaled within slop.
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index ef9b457..dd03366 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -16,6 +16,7 @@
#include "ScreenCaptureOutput.h"
#include "ScreenCaptureRenderSurface.h"
+#include "ui/Rotation.h"
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
@@ -24,24 +25,6 @@
namespace android {
-namespace {
-
-ui::Size getDisplaySize(ui::Rotation orientation, const Rect& sourceCrop) {
- if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
- return {sourceCrop.getHeight(), sourceCrop.getWidth()};
- }
- return {sourceCrop.getWidth(), sourceCrop.getHeight()};
-}
-
-Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int reqHeight) {
- if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) {
- return {reqHeight, reqWidth};
- }
- return {reqWidth, reqHeight};
-}
-
-} // namespace
-
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
@@ -49,6 +32,7 @@
bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling,
args.dimInGammaSpaceForEnhancedScreenshots);
output->editState().isSecure = args.renderArea.isSecure();
+ output->editState().isProtected = args.isProtected;
output->setCompositionEnabled(true);
output->setLayerFilter({args.layerStack});
output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
@@ -62,11 +46,10 @@
.Build()));
const Rect& sourceCrop = args.renderArea.getSourceCrop();
- const ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags());
- output->setDisplaySize(getDisplaySize(orientation, sourceCrop));
+ const ui::Rotation orientation = ui::ROTATION_0;
+ output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
output->setProjection(orientation, sourceCrop,
- getOrientedDisplaySpaceRect(orientation, args.renderArea.getReqWidth(),
- args.renderArea.getReqHeight()));
+ {args.renderArea.getReqWidth(), args.renderArea.getReqHeight()});
{
std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
@@ -92,10 +75,10 @@
outputState.renderIntent = mColorProfile.renderIntent;
}
-renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisplaySettings()
- const {
+renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisplaySettings(
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer) const {
auto clientCompositionDisplay =
- compositionengine::impl::Output::generateClientCompositionDisplaySettings();
+ compositionengine::impl::Output::generateClientCompositionDisplaySettings(buffer);
clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
auto renderIntent = static_cast<ui::RenderIntent>(clientCompositionDisplay.renderIntent);
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index fc095de..069f458 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -38,6 +38,7 @@
bool regionSampling;
bool treat170mAsSrgb;
bool dimInGammaSpaceForEnhancedScreenshots;
+ bool isProtected = false;
};
// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
@@ -58,7 +59,8 @@
protected:
bool getSkipColorTransform() const override { return false; }
- renderengine::DisplaySettings generateClientCompositionDisplaySettings() const override;
+ renderengine::DisplaySettings generateClientCompositionDisplaySettings(
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer) const override;
private:
const RenderArea& mRenderArea;
diff --git a/services/surfaceflinger/ScreenCaptureRenderSurface.h b/services/surfaceflinger/ScreenCaptureRenderSurface.h
index 2097300..50ba9bf 100644
--- a/services/surfaceflinger/ScreenCaptureRenderSurface.h
+++ b/services/surfaceflinger/ScreenCaptureRenderSurface.h
@@ -37,7 +37,7 @@
return mBuffer;
}
- void queueBuffer(base::unique_fd readyFence) override {
+ void queueBuffer(base::unique_fd readyFence, float) override {
mRenderFence = sp<Fence>::make(readyFence.release());
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index fc51721..c3bfb58 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>
@@ -112,6 +113,7 @@
#include <unordered_map>
#include <vector>
+#include <common/FlagManager.h>
#include <gui/LayerStatePermissions.h>
#include <gui/SchedulingPolicy.h>
#include <ui/DisplayIdentification.h>
@@ -128,7 +130,6 @@
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
#include "Effects/Daltonizer.h"
-#include "FlagManager.h"
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
@@ -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,66 @@
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);
+}
+
+bool isFrameIntervalOnCadence(TimePoint expectedPresentTime, TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval, Period timeout, 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(TimePoint expectedPresentTime,
+ TimePoint lastExpectedPresentTimestamp,
+ std::optional<Period> timeoutOpt, 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 anonymous
// ---------------------------------------------------------------------------
@@ -496,10 +552,11 @@
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();
+ // These are set by the HWC implementation to indicate that they will use the workarounds.
+ mIsHotplugErrViaNegVsync =
+ base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
+
+ mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -561,6 +618,9 @@
// Display ID is assigned when virtual display is allocated by HWC.
DisplayDeviceState state;
state.isSecure = secure;
+ // Set display as protected when marked as secure to ensure no behavior change
+ // TODO (b/314820005): separate as a different arg when creating the display.
+ state.isProtected = secure;
state.displayName = displayName;
state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
mCurrentState.displays.add(token, state);
@@ -675,6 +735,7 @@
return;
}
mBootFinished = true;
+ FlagManager::getMutableInstance().markBootCompleted();
if (mStartPropertySetThread->join() != NO_ERROR) {
ALOGE("Join StartPropertySetThread failed!");
}
@@ -688,7 +749,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");
@@ -708,7 +769,7 @@
sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
@@ -716,10 +777,12 @@
}
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();
+ // Ordering is important here, as onBootFinished signals to PowerAdvisor that concurrency
+ // is safe because its variables are initialized.
+ mPowerAdvisor->onBootFinished();
ALOGD("Power hint is %s",
hintSessionUsed ? "supported" : (hintSessionEnabled ? "unsupported" : "disabled"));
if (hintSessionUsed) {
@@ -729,7 +792,7 @@
if (renderEngineTid.has_value()) {
tidList.emplace_back(*renderEngineTid);
}
- if (!mPowerAdvisor->startPowerHintSession(tidList)) {
+ if (!mPowerAdvisor->startPowerHintSession(std::move(tidList))) {
ALOGW("Cannot start power hint session");
}
}
@@ -741,10 +804,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>
@@ -874,7 +933,7 @@
mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache(shouldPrimeUltraHDR);
if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Can't set SCHED_OTHER for primeCache");
+ ALOGW("Can't set SCHED_FIFO after primeCache");
}
}
@@ -899,7 +958,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;
}
@@ -1049,7 +1108,7 @@
outMode.peakRefreshRate = peakFps.getValue();
outMode.vsyncRate = mode->getVsyncRate().getValue();
- const auto vsyncConfigSet = mVsyncConfiguration->getConfigsForRefreshRate(
+ const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(
Fps::fromValue(outMode.peakRefreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
@@ -1176,7 +1235,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
+void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) {
const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
@@ -1189,10 +1248,9 @@
const auto mode = request.mode;
const bool emitEvent = request.emitEvent;
- switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)),
- force)) {
- case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
- // Set the render rate as setDesiredActiveMode updated it.
+ switch (display->setDesiredMode(std::move(request), force)) {
+ case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
+ // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId,
display->refreshRateSelector().getActiveMode().fps);
@@ -1202,31 +1260,30 @@
// Start receiving vsync samples now, so that we can detect a period
// switch.
mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
- mode.modePtr->getVsyncRate());
+ mode.modePtr.get());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
if (displayId == mActiveDisplayId) {
- updatePhaseConfiguration(mode.fps);
+ mScheduler->updatePhaseConfiguration(mode.fps);
}
mScheduler->setModeChangePending(true);
break;
- case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
+ case DisplayDevice::DesiredModeAction::InitiateRenderRateSwitch:
mScheduler->setRenderRate(displayId, mode.fps);
if (displayId == mActiveDisplayId) {
- updatePhaseConfiguration(mode.fps);
- mRefreshRateStats->setRefreshRate(mode.fps);
+ mScheduler->updatePhaseConfiguration(mode.fps);
}
if (emitEvent) {
dispatchDisplayModeChangeEvent(displayId, mode);
}
break;
- case DisplayDevice::DesiredActiveModeAction::None:
+ case DisplayDevice::DesiredModeAction::None:
break;
}
}
@@ -1240,7 +1297,7 @@
}
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
const auto displayOpt =
FTL_FAKE_GUARD(mStateLock,
ftl::find_if(mPhysicalDisplays,
@@ -1286,60 +1343,61 @@
const auto displayId = display.getPhysicalId();
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto upcomingModeInfo = display.getUpcomingActiveMode();
- if (!upcomingModeInfo.modeOpt) {
+ const auto pendingModeOpt = display.getPendingMode();
+ if (!pendingModeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
return;
}
- if (display.getActiveMode().modePtr->getResolution() !=
- upcomingModeInfo.modeOpt->modePtr->getResolution()) {
+ const auto& activeMode = pendingModeOpt->mode;
+
+ if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) {
auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
- state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get();
+ state.physical->activeMode = activeMode.modePtr.get();
processDisplayChangesLocked();
// processDisplayChangesLocked will update all necessary components so we're done here.
return;
}
- const auto& activeMode = *upcomingModeInfo.modeOpt;
display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
activeMode.fps);
if (displayId == mActiveDisplayId) {
- mRefreshRateStats->setRefreshRate(activeMode.fps);
- updatePhaseConfiguration(activeMode.fps);
+ mScheduler->updatePhaseConfiguration(activeMode.fps);
}
- if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
+ if (pendingModeOpt->emitEvent) {
dispatchDisplayModeChangeEvent(displayId, activeMode);
}
}
-void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
- display->clearDesiredActiveModeState();
+void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) {
+ display->clearDesiredMode();
if (display->getPhysicalId() == mActiveDisplayId) {
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
-void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
- const auto desiredActiveMode = display->getDesiredActiveMode();
- const auto& modeOpt = desiredActiveMode->modeOpt;
- const auto displayId = modeOpt->modePtr->getPhysicalDisplayId();
- const auto vsyncRate = modeOpt->modePtr->getVsyncRate();
- const auto renderFps = modeOpt->fps;
- clearDesiredActiveModeState(display);
- mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, vsyncRate);
+void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) {
+ const auto activeModeOpt = display->getDesiredMode();
+ auto activeModePtr = activeModeOpt->mode.modePtr;
+ const auto displayId = activeModePtr->getPhysicalDisplayId();
+ const auto renderFps = activeModeOpt->mode.fps;
+
+ dropModeRequest(display);
+
+ constexpr bool kAllowToEnable = true;
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
mScheduler->setRenderRate(displayId, renderFps);
if (displayId == mActiveDisplayId) {
- updatePhaseConfiguration(renderFps);
+ mScheduler->updatePhaseConfiguration(renderFps);
}
}
@@ -1352,25 +1410,23 @@
const auto display = getDisplayDeviceLocked(id);
if (!display) continue;
- // Store the local variable to release the lock.
- const auto desiredActiveMode = display->getDesiredActiveMode();
- if (!desiredActiveMode) {
- // No desired active mode pending to be applied.
+ auto desiredModeOpt = display->getDesiredMode();
+ if (!desiredModeOpt) {
continue;
}
if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
- clearDesiredActiveModeState(display);
+ dropModeRequest(display);
continue;
}
- const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
+ const auto desiredModeId = desiredModeOpt->mode.modePtr->getId();
const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
desiredModeId.value());
- clearDesiredActiveModeState(display);
+ dropModeRequest(display);
continue;
}
@@ -1378,19 +1434,16 @@
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
to_string(display->getId()).c_str());
- if (display->getActiveMode() == desiredActiveMode->modeOpt) {
- // we are already in the requested mode, there is nothing left to do
- desiredActiveModeChangeDone(display);
+ if (display->getActiveMode() == desiredModeOpt->mode) {
+ applyActiveMode(display);
continue;
}
// Desired active mode was set, it is different than the mode currently in use, however
// allowed modes might have changed by the time we process the refresh.
// Make sure the desired mode is still allowed
- const auto displayModeAllowed =
- display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt);
- if (!displayModeAllowed) {
- clearDesiredActiveModeState(display);
+ if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) {
+ dropModeRequest(display);
continue;
}
@@ -1400,13 +1453,7 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- const auto status =
- display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline);
-
- if (status != NO_ERROR) {
- // initiateModeChange may fail if a hotplug event is just about
- // to be sent. We just log the error in this case.
- ALOGW("initiateModeChange failed: %d", status);
+ if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) {
continue;
}
@@ -1427,16 +1474,16 @@
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
finalizeDisplayModeChange(*display);
- const auto desiredActiveMode = display->getDesiredActiveMode();
- if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
- desiredActiveModeChangeDone(display);
+ const auto desiredModeOpt = display->getDesiredMode();
+ if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) {
+ applyActiveMode(display);
}
}
}
void SurfaceFlinger::disableExpensiveRendering() {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
ATRACE_NAME(whence);
if (mPowerAdvisor->isUsingExpensiveRendering()) {
for (const auto& [_, display] : mDisplays) {
@@ -1478,7 +1525,7 @@
}
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto displayOpt =
ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
@@ -1559,7 +1606,7 @@
status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
const auto snapshotOpt =
ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
.transform(&ftl::to_mapped_ref<PhysicalDisplays>)
@@ -1587,7 +1634,7 @@
status_t SurfaceFlinger::clearBootDisplayMode(const sp<IBinder>& displayToken) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().clearBootDisplayMode(*displayId);
} else {
@@ -1626,7 +1673,7 @@
ALOGE("hdrOutputConversion is not supported by this device.");
return INVALID_OPERATION;
}
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
using AidlHdrConversionStrategy =
aidl::android::hardware::graphics::common::HdrConversionStrategy;
using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
@@ -1686,7 +1733,7 @@
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
@@ -1697,7 +1744,7 @@
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
@@ -1751,7 +1798,7 @@
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) -> status_t {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
componentMask, maxFrames);
@@ -1804,7 +1851,7 @@
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
outLayers->clear();
- auto future = mScheduler->schedule([=] {
+ auto future = mScheduler->schedule([=, this] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
mDrawingState.traverseInZOrder([&](Layer* layer) {
outLayers->push_back(layer->getLayerDebugInfo(display.get()));
@@ -1910,7 +1957,7 @@
}
const char* const whence = __func__;
- return ftl::Future(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ return ftl::Future(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
const bool supportsDisplayBrightnessCommand =
getHwComposer().getComposer()->isSupported(
@@ -2087,15 +2134,28 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- if (mConnectedDisplayFlagValue) {
+ if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
+ vsyncPeriod.has_value()) {
// use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
- if (mIsHotplugErrViaNegVsync && timestamp < 0 && vsyncPeriod.has_value() &&
- vsyncPeriod.value() == ~0) {
- int hotplugErrorCode = static_cast<int32_t>(-timestamp);
- ALOGD("SurfaceFlinger got hotplugErrorCode=%d", hotplugErrorCode);
+ if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) {
+ const int32_t hotplugErrorCode = static_cast<int32_t>(-timestamp);
+ ALOGD("SurfaceFlinger got hotplugErrorCode=%d for display %" PRIu64, hotplugErrorCode,
+ hwcDisplayId);
mScheduler->onHotplugConnectionError(mAppConnectionHandle, hotplugErrorCode);
return;
}
+
+ if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
+ const int32_t value = static_cast<int32_t>(-timestamp);
+ // one byte is good enough to encode android.hardware.drm.HdcpLevel
+ const int32_t maxLevel = (value >> 8) & 0xFF;
+ const int32_t connectedLevel = value & 0xFF;
+ ALOGD("SurfaceFlinger got HDCP level changed: connected=%d, max=%d for "
+ "display=%" PRIu64,
+ connectedLevel, maxLevel, hwcDisplayId);
+ updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
+ return;
+ }
}
ATRACE_NAME(vsyncPeriod
@@ -2111,15 +2171,28 @@
}
}
-void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) {
- {
- std::lock_guard<std::mutex> lock(mHotplugMutex);
- mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
+void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId,
+ DisplayHotplugEvent event) {
+ if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) {
+ hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED)
+ ? hal::Connection::CONNECTED
+ : hal::Connection::DISCONNECTED;
+ {
+ std::lock_guard<std::mutex> lock(mHotplugMutex);
+ mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
+ }
+
+ if (mScheduler) {
+ mScheduler->scheduleConfigure();
+ }
+
+ return;
}
- if (mScheduler) {
- mScheduler->scheduleConfigure();
+ if (FlagManager::getInstance().hotplug2()) {
+ ALOGD("SurfaceFlinger got hotplug event=%d", static_cast<int32_t>(event));
+ // TODO(b/311403559): use enum type instead of int
+ mScheduler->onHotplugConnectionError(mAppConnectionHandle, static_cast<int32_t>(event));
}
}
@@ -2151,19 +2224,16 @@
void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
ATRACE_CALL();
if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
- const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos);
- ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue());
- static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
- {
- {
- const auto display = getDisplayDeviceLocked(*displayId);
- FTL_FAKE_GUARD(kMainThreadContext,
- display->updateRefreshRateOverlayRate(fps,
- display->getActiveMode()
- .fps,
- /* setByHwc */ true));
- }
- }
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
+ const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported()
+ ? data.refreshPeriodNanos
+ : data.vsyncPeriodNanos);
+ ATRACE_FORMAT("%s Fps %d", whence, fps.getIntValue());
+ const auto display = getDisplayDeviceLocked(*displayId);
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->updateRefreshRateOverlayRate(fps, display->getActiveMode().fps,
+ /* setByHwc */ true));
}));
}
}
@@ -2213,12 +2283,13 @@
continue;
}
- const bool updateSmallDirty = mScheduler->supportSmallDirtyDetection() &&
+ const bool updateSmallDirty = FlagManager::getInstance().enable_small_area_detection() &&
((snapshot->clientChanges & layer_state_t::eSurfaceDamageRegionChanged) ||
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;
@@ -2227,8 +2298,9 @@
}
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldn't find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldn't find layer object for %s",
+ snapshot->getDebugString().c_str());
if (updateSmallDirty) {
// Update small dirty flag while surface damage region or geometry changed
@@ -2246,8 +2318,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);
@@ -2312,11 +2389,7 @@
mLegacyLayers[layer->sequence] = layer;
}
}
- if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
- ATRACE_NAME("LayerHierarchyBuilder:update");
- mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
- mLayerLifecycleManager.getDestroyedLayers());
- }
+ mLayerHierarchyBuilder.update(mLayerLifecycleManager);
}
bool mustComposite = false;
@@ -2376,8 +2449,9 @@
const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
auto it = mLegacyLayers.find(layer->id);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- layer->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ layer->getDebugString().c_str());
if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) {
if (!it->second->hasBuffer()) {
// The last latch time is used to classify a missed frame as buffer stuffing
@@ -2480,6 +2554,10 @@
if (pacesetterFrameTarget.isFramePending()) {
if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
+ if (FlagManager::getInstance().vrr_config()) {
+ mScheduler->getVsyncSchedule()->getTracker().onFrameMissed(
+ pacesetterFrameTarget.expectedPresentTime());
+ }
scheduleCommit(FrameHint::kNone);
return false;
}
@@ -2603,6 +2681,8 @@
if (const auto display = getCompositionDisplayLocked(id)) {
refreshArgs.outputs.push_back(display);
}
+
+ refreshArgs.frameTargets.try_emplace(id, &targeter->target());
}
std::vector<DisplayId> displayIds;
@@ -2671,22 +2751,23 @@
refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
- const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
-
- if (!getHwComposer().getComposer()->isSupported(
- Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- pacesetterTarget.wouldPresentEarly(vsyncPeriod)) {
- const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
-
- // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget.
- refreshArgs.earliestPresentTime =
- pacesetterTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
- }
-
+ const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime();
+ // TODO(b/255601557) Update frameInterval per display
+ refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime);
refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
- refreshArgs.expectedPresentTime = pacesetterTarget.expectedPresentTime().ns();
refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
-
+ {
+ auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId];
+ auto lastExpectedPresentTimestamp = TimePoint::fromNs(
+ notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns());
+ if (expectedPresentTime > lastExpectedPresentTimestamp) {
+ // If the values are not same, then hint is sent with newer value.
+ // And because composition always follows the notifyExpectedPresentIfRequired, we can
+ // skip updating the lastExpectedPresentTimestamp in this case.
+ notifyExpectedPresentData.lastExpectedPresentTimestamp
+ .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime);
+ }
+ }
// Store the present time just before calling to the composition engine so we could notify
// the scheduler.
const auto presentTime = systemTime();
@@ -2746,11 +2827,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 +2988,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;
@@ -2971,7 +3051,8 @@
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
const Period vsyncPeriod = schedule->period();
- const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset;
+ const nsecs_t vsyncPhase =
+ mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset;
const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
presentLatency.ns());
@@ -3003,8 +3084,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());
}
@@ -3069,9 +3151,9 @@
[&, compositionDisplay = compositionDisplay](
std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
auto& legacyLayer = it->second;
sp<LayerFE> layerFe =
legacyLayer->getCompositionEngineLayerFE(snapshot->path);
@@ -3353,9 +3435,10 @@
}
const sp<IBinder> token = sp<BBinder>::make();
+ const ui::DisplayConnectionType connectionType =
+ getHwComposer().getDisplayConnectionType(displayId);
- mPhysicalDisplays.try_emplace(displayId, token, displayId,
- getHwComposer().getDisplayConnectionType(displayId),
+ mPhysicalDisplays.try_emplace(displayId, token, displayId, connectionType,
std::move(displayModes), std::move(colorModes),
std::move(info.deviceProductInfo));
@@ -3363,7 +3446,8 @@
state.physical = {.id = displayId,
.hwcDisplayId = hwcDisplayId,
.activeMode = std::move(activeMode)};
- state.isSecure = true; // All physical displays are currently considered secure.
+ state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
+ state.isProtected = true;
state.displayName = std::move(info.name);
mCurrentState.displays.add(token, state);
@@ -3395,6 +3479,7 @@
displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
creationArgs.isSecure = state.isSecure;
+ creationArgs.isProtected = state.isProtected;
creationArgs.displaySurface = displaySurface;
creationArgs.hasWideColorGamut = false;
creationArgs.supportedPerFrameMetadata = 0;
@@ -3526,6 +3611,7 @@
builder.setPixels(resolution);
builder.setIsSecure(state.isSecure);
+ builder.setIsProtected(state.isProtected);
builder.setPowerAdvisor(mPowerAdvisor.get());
builder.setName(state.displayName);
auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -3643,7 +3729,7 @@
// TODO(b/175678251) Call a listener instead.
if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
- resetPhaseConfiguration(display->getActiveMode().fps);
+ mScheduler->resetPhaseConfiguration(display->getActiveMode().fps);
}
}
return;
@@ -3679,15 +3765,6 @@
}
}
-void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) {
- // Cancel the pending refresh rate change, if any, before updating the phase configuration.
- mScheduler->vsyncModulator().cancelRefreshRateChange();
-
- mVsyncConfiguration->reset();
- updatePhaseConfiguration(refreshRate);
- mRefreshRateStats->setRefreshRate(refreshRate);
-}
-
void SurfaceFlinger::processDisplayChangesLocked() {
// here we take advantage of Vector's copy-on-write semantics to
// improve performance by skipping the transaction entirely when
@@ -3798,7 +3875,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());
@@ -4006,7 +4082,7 @@
}
if (display->refreshRateSelector().isModeAllowed(request.mode)) {
- setDesiredActiveMode(std::move(request));
+ setDesiredMode(std::move(request));
} else {
ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
to_string(displayId).c_str());
@@ -4035,6 +4111,69 @@
}
}
+void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime,
+ ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) {
+ const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod();
+ const auto timeoutOpt = [&]() -> std::optional<Period> {
+ const auto vrrConfig = modePtr->getVrrConfig();
+ if (!vrrConfig) return std::nullopt;
+
+ const auto notifyExpectedPresentConfig =
+ modePtr->getVrrConfig()->notifyExpectedPresentConfig;
+ if (!notifyExpectedPresentConfig) return std::nullopt;
+ return Period::fromNs(notifyExpectedPresentConfig->timeoutNs);
+ }();
+
+ notifyExpectedPresentIfRequired(modePtr->getPhysicalDisplayId(), vsyncPeriod,
+ expectedPresentTime, renderRate, timeoutOpt);
+}
+
+void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId,
+ Period vsyncPeriod,
+ TimePoint expectedPresentTime,
+ Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
+ {
+ auto& data = mNotifyExpectedPresentMap[displayId];
+ const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load();
+ const auto lastFrameInterval = data.lastFrameInterval;
+ data.lastFrameInterval = frameInterval;
+ const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2);
+
+ const constexpr nsecs_t kOneSecondNs =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ const auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns()
+ : kOneSecondNs);
+ const bool frameIntervalIsOnCadence =
+ isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp,
+ lastFrameInterval, timeout, threshold);
+
+ const bool expectedPresentWithinTimeout =
+ isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+ timeoutOpt, threshold);
+
+ using fps_approx_ops::operator!=;
+ if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
+
+ if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ return;
+ }
+ data.lastExpectedPresentTimestamp = expectedPresentTime;
+ }
+
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
+ frameInterval);
+ if (status != NO_ERROR) {
+ ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
+ displayId.value);
+ }
+ }));
+}
+
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
using namespace scheduler;
@@ -4042,16 +4181,15 @@
const auto activeMode = display->refreshRateSelector().getActiveMode();
const Fps activeRefreshRate = activeMode.fps;
- mRefreshRateStats =
- std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF);
-
- mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
FeatureFlags features;
- if (sysprop::use_content_detection_for_refresh_rate(false)) {
+ const auto defaultContentDetectionValue =
+ FlagManager::getInstance().enable_fro_dependent_features() &&
+ sysprop::enable_frame_rate_override(true);
+ if (sysprop::use_content_detection_for_refresh_rate(defaultContentDetectionValue)) {
features |= Feature::kContentDetection;
- if (flags::enable_small_area_detection()) {
+ if (FlagManager::getInstance().enable_small_area_detection()) {
features |= Feature::kSmallDirtyContentDetection;
}
}
@@ -4068,16 +4206,22 @@
if (mBackpressureGpuComposition) {
features |= Feature::kBackpressureGpuComposition;
}
-
- auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
+ if (getHwComposer().getComposer()->isSupported(
+ Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) {
+ features |= Feature::kExpectedPresentTime;
+ }
mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this), features,
- std::move(modulatorPtr));
+ getFactory(), activeRefreshRate, *mTimeStats,
+ 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();
+ const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
mAppConnectionHandle =
mScheduler->createEventThread(Scheduler::Cycle::Render,
@@ -4097,15 +4241,6 @@
sp<RegionSamplingThread>::make(*this,
RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
-
- mIsHotplugErrViaNegVsync =
- base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
-}
-
-void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) {
- mVsyncConfiguration->setRefreshRateFps(refreshRate);
- mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(),
- refreshRate.getPeriod());
}
void SurfaceFlinger::doCommitTransactions() {
@@ -4287,7 +4422,7 @@
if (mNumLayers >= MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
MAX_LAYERS);
- static_cast<void>(mScheduler->schedule([=] {
+ static_cast<void>(mScheduler->schedule([=, this] {
ALOGE("Dumping layer keeping > 20 children alive:");
bool leakingParentLayerFound = false;
mDrawingState.traverse([&](Layer* layer) {
@@ -4308,7 +4443,8 @@
int sampleSize = (layer->getChildrenCount() / 100) + 1;
layer->traverseChildren([&](Layer* layer) {
if (rand() % sampleSize == 0) {
- ALOGE("Child Layer: %s", layer->getName().c_str());
+ ALOGE("Child Layer: %s%s", layer->getName().c_str(),
+ (layer->isHandleAlive() ? "handleAlive" : ""));
}
});
}
@@ -4677,7 +4813,7 @@
return false;
}
- const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->period() / 2;
+ const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->minFramePeriod() / 2;
return predictedPresentTime >= expectedPresentTime &&
predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
@@ -4879,6 +5015,7 @@
.transform = layer->getTransform(),
.setFrameRateVote = layer->getFrameRateForLayerTree(),
.frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ .isFrontBuffered = layer->isFrontBuffered(),
};
layer->recordLayerHistoryAnimationTx(layerProps, now);
}
@@ -5745,7 +5882,7 @@
display->setPowerMode(mode);
- const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getVsyncRate();
+ const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
// Turn on the display
@@ -5774,22 +5911,25 @@
}
getHwComposer().setPowerMode(displayId, mode);
- if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
+ if (mode != hal::PowerMode::DOZE_SUSPEND &&
+ (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
const bool enable =
mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
requestHardwareVsync(displayId, enable);
- mScheduler->enableSyntheticVsync(false);
+ if (displayId == mActiveDisplayId) {
+ mScheduler->enableSyntheticVsync(false);
+ }
constexpr bool kAllowToEnable = true;
- mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, refreshRate);
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
}
mVisibleRegionsDirty = true;
scheduleComposite(FrameHint::kActive);
} else if (mode == hal::PowerMode::OFF) {
+ const bool currentModeNotDozeSuspend = (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND);
// Turn off the display
-
if (displayId == mActiveDisplayId) {
if (const auto display = getActivatableDisplay()) {
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
@@ -5803,14 +5943,24 @@
strerror(errno));
}
- if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
- mScheduler->disableHardwareVsync(displayId, true);
+ if (currentModeNotDozeSuspend) {
+ if (!FlagManager::getInstance().multithreaded_present()) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ }
mScheduler->enableSyntheticVsync();
}
}
}
+ if (currentModeNotDozeSuspend && FlagManager::getInstance().multithreaded_present()) {
+ constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(displayId, kDisallow);
+ }
- // Disable VSYNC before turning off the display.
+ // We must disable VSYNC *before* turning off the display. The call to
+ // disableHardwareVsync, above, schedules a task to turn it off after
+ // this method returns. But by that point, the display is OFF, so the
+ // call just updates the pending state, without actually disabling
+ // VSYNC.
requestHardwareVsync(displayId, false);
getHwComposer().setPowerMode(displayId, mode);
@@ -5819,17 +5969,24 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (displayId == mActiveDisplayId && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
- ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
- mVisibleRegionsDirty = true;
- scheduleRepaint();
- mScheduler->enableSyntheticVsync(false);
- mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate);
+ if (*currentModeOpt == hal::PowerMode::DOZE_SUSPEND &&
+ (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
+ if (displayId == mActiveDisplayId) {
+ ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
+ mVisibleRegionsDirty = true;
+ scheduleRepaint();
+ mScheduler->enableSyntheticVsync(false);
+ }
+ constexpr bool kAllowToEnable = true;
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
+ if (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present()) {
+ constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(displayId, kDisallow);
+ }
if (displayId == mActiveDisplayId) {
- mScheduler->disableHardwareVsync(displayId, true);
mScheduler->enableSyntheticVsync();
}
getHwComposer().setPowerMode(displayId, mode);
@@ -5840,7 +5997,7 @@
if (displayId == mActiveDisplayId) {
mTimeStats->setPowerMode(mode);
- mRefreshRateStats->setPowerMode(mode);
+ mScheduler->setActiveDisplayPowerModeForRefreshRateStats(mode);
}
mScheduler->setDisplayPowerMode(displayId, mode);
@@ -5849,7 +6006,7 @@
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
kMainThreadContext) {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
@@ -5876,138 +6033,60 @@
!PermissionCache::checkPermission(sDump, pid, uid)) {
StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
pid, uid);
- } else {
- Dumper hwclayersDump = [this](const DumpArgs&, bool, std::string& result)
- FTL_FAKE_GUARD(mStateLock) -> void const {
- if (mLayerLifecycleManagerEnabled) {
- mScheduler
- ->schedule([this, &result]() FTL_FAKE_GUARD(kMainThreadContext)
- FTL_FAKE_GUARD(mStateLock) {
- dumpHwcLayersMinidump(result);
- })
- .get();
- } else {
- dumpHwcLayersMinidumpLockedLegacy(result);
- }
- };
-
- static const std::unordered_map<std::string, Dumper> dumpers = {
- {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
- {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
- {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
- {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
- {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
- {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
- {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
- {"--hwclayers"s, std::move(hwclayersDump)},
- {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
- {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
- {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
- {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
- {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
- {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
- {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
- {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
- {"--frontend"s, dumper(&SurfaceFlinger::dumpFrontEnd)},
- };
-
- const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
-
- // Traversal of drawing state must happen on the main thread.
- // Otherwise, SortedVector may have shared ownership during concurrent
- // traversals, which can result in use-after-frees.
- std::string compositionLayers;
- mScheduler
- ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- if (!mLayerLifecycleManagerEnabled) {
- StringAppendF(&compositionLayers, "Composition layers\n");
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- auto* compositionState = layer->getCompositionState();
- if (!compositionState || !compositionState->isVisible) return;
- android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n",
- layer,
- layer->getDebugName()
- ? layer->getDebugName()
- : "<unknown>");
- compositionState->dump(compositionLayers);
- });
- } else {
- std::ostringstream out;
- out << "\nComposition list\n";
- ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
- if (snapshot->hasSomethingToDraw()) {
- if (lastPrintedLayerStackHeader !=
- snapshot->outputFilter.layerStack) {
- lastPrintedLayerStackHeader =
- snapshot->outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id
- << "\n";
- }
- out << " " << *snapshot << "\n";
- }
- });
-
- out << "\nInput list\n";
- lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachInputSnapshot(
- [&](const frontend::LayerSnapshot& snapshot) {
- if (lastPrintedLayerStackHeader !=
- snapshot.outputFilter.layerStack) {
- lastPrintedLayerStackHeader =
- snapshot.outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id
- << "\n";
- }
- out << " " << snapshot << "\n";
- });
-
- out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy() << "\n\n";
- compositionLayers = out.str();
- dumpHwcLayersMinidump(compositionLayers);
- }
- })
- .get();
-
- bool dumpLayers = true;
- {
- TimedLock lock(mStateLock, s2ns(1), __func__);
- if (!lock.locked()) {
- StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
- strerror(-lock.status), lock.status);
- }
-
- if (const auto it = dumpers.find(flag); it != dumpers.end()) {
- (it->second)(args, asProto, result);
- dumpLayers = false;
- } else if (!asProto) {
- dumpAllLocked(args, compositionLayers, result);
- }
- }
-
- if (dumpLayers) {
- perfetto::protos::LayersTraceFileProto traceFileProto =
- mLayerTracing.createTraceFileProto();
- perfetto::protos::LayersSnapshotProto* layersTrace = traceFileProto.add_entry();
- perfetto::protos::LayersProto layersProto = dumpProtoFromMainThread();
- layersTrace->mutable_layers()->Swap(&layersProto);
- auto displayProtos = dumpDisplayProto();
- layersTrace->mutable_displays()->Swap(&displayProtos);
-
- if (asProto) {
- result.append(traceFileProto.SerializeAsString());
- } else {
- // Dump info that we need to access from the main thread
- const auto layerTree = LayerProtoParser::generateLayerTree(layersTrace->layers());
- result.append(LayerProtoParser::layerTreeToString(layerTree));
- result.append("\n");
- dumpOffscreenLayers(result);
- }
- }
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
}
+ if (asProto && args.empty()) {
+ perfetto::protos::LayersTraceFileProto traceFileProto =
+ mLayerTracing.createTraceFileProto();
+ perfetto::protos::LayersSnapshotProto* layersTrace = traceFileProto.add_entry();
+ perfetto::protos::LayersProto layersProto = dumpProtoFromMainThread();
+ layersTrace->mutable_layers()->Swap(&layersProto);
+ auto displayProtos = dumpDisplayProto();
+ layersTrace->mutable_displays()->Swap(&displayProtos);
+ result.append(traceFileProto.SerializeAsString());
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
+ }
+
+ static const std::unordered_map<std::string, Dumper> dumpers = {
+ {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
+ {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
+ {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
+ {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
+ {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+ {"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)},
+ {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
+ {"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)},
+ {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
+ {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
+ {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
+ {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
+ {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
+ {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ };
+
+ const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
+ if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+ (it->second)(args, asProto, result);
+ write(fd, result.c_str(), result.size());
+ return NO_ERROR;
+ }
+
+ // Traversal of drawing state must happen on the main thread.
+ // Otherwise, SortedVector may have shared ownership during concurrent
+ // traversals, which can result in use-after-frees.
+ std::string compositionLayers;
+ mScheduler
+ ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+ dumpVisibleFrontEnd(compositionLayers);
+ })
+ .get();
+ dumpAll(args, compositionLayers, result);
write(fd, result.c_str(), result.size());
return NO_ERROR;
}
@@ -6083,10 +6162,6 @@
dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
dumper.eol();
- mRefreshRateStats->dump(result);
- dumper.eol();
-
- mVsyncConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64
" ns\n\n",
@@ -6219,35 +6294,81 @@
}
void SurfaceFlinger::dumpFrontEnd(std::string& result) {
- mScheduler
- ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- std::ostringstream out;
- out << "\nComposition list\n";
- ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
- lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << *snapshot << "\n";
+ }
+
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
+
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy().dump() << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n";
+ result.append(out.str());
+}
+
+void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) {
+ if (!mLayerLifecycleManagerEnabled) {
+ StringAppendF(&result, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto* compositionState = layer->getCompositionState();
+ if (!compositionState || !compositionState->isVisible) return;
+ android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
+ layer->getDebugName() ? layer->getDebugName()
+ : "<unknown>");
+ compositionState->dump(result);
+ });
+
+ StringAppendF(&result, "Offscreen Layers\n");
+ for (Layer* offscreenLayer : mOffscreenLayers) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
+ }
+ } else {
+ std::ostringstream out;
+ out << "\nComposition list\n";
+ ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (snapshot->hasSomethingToDraw()) {
+ if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot->outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << *snapshot << "\n";
}
- out << " " << *snapshot << "\n";
- }
+ });
- out << "\nInput list\n";
- lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
- mLayerSnapshotBuilder.forEachInputSnapshot(
- [&](const frontend::LayerSnapshot& snapshot) {
- if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
- lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
- out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
- }
- out << " " << snapshot << "\n";
- });
+ out << "\nInput list\n";
+ lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
+ mLayerSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (lastPrintedLayerStackHeader != snapshot.outputFilter.layerStack) {
+ lastPrintedLayerStackHeader = snapshot.outputFilter.layerStack;
+ out << "LayerStack=" << lastPrintedLayerStackHeader.id << "\n";
+ }
+ out << " " << snapshot << "\n";
+ });
- out << "\nLayer Hierarchy\n"
- << mLayerHierarchyBuilder.getHierarchy().dump() << "\n\n";
- result.append(out.str());
- })
- .get();
+ out << "\nLayer Hierarchy\n"
+ << mLayerHierarchyBuilder.getHierarchy() << "\nOffscreen Hierarchy\n"
+ << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n";
+ result = out.str();
+ dumpHwcLayersMinidump(result);
+ }
}
perfetto::protos::LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
@@ -6330,7 +6451,7 @@
}
perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
- return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
+ return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get();
}
void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
@@ -6348,7 +6469,7 @@
}
void SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy(std::string& result) const {
- for (const auto& [token, display] : mDisplays) {
+ for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
@@ -6365,7 +6486,10 @@
}
void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
- for (const auto& [token, display] : mDisplays) {
+ if (!mLayerLifecycleManagerEnabled) {
+ return dumpHwcLayersMinidumpLockedLegacy(result);
+ }
+ for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
@@ -6382,16 +6506,23 @@
return;
}
auto it = mLegacyLayers.find(snapshot.sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot.getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
it->second->miniDump(result, snapshot, ref);
});
result.append("\n");
}
}
-void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
- std::string& result) const {
+void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositionLayers,
+ std::string& result) const {
+ TimedLock lock(mStateLock, s2ns(1), __func__);
+ if (!lock.locked()) {
+ StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+ strerror(-lock.status), lock.status);
+ }
+
const bool colorize = !args.empty() && args[0] == String16("--color");
Colorizer colorizer(colorize);
@@ -6461,17 +6592,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 +6668,7 @@
/*
* Dump flag/property manager state
*/
- mFlagManager.dump(result);
+ FlagManager::getInstance().dump(result);
result.append(mTimeStats->miniDump());
result.append("\n");
@@ -6746,8 +6866,7 @@
case 1007: // Unused.
return NAME_NOT_FOUND;
case 1008: // Toggle forced GPU composition.
- mDebugDisableHWC = data.readInt32() != 0;
- scheduleRepaint();
+ sfdo_forceClientComposition(data.readInt32() != 0);
return NO_ERROR;
case 1009: // Toggle use of transform hint.
mDebugDisableTransformHint = data.readInt32() != 0;
@@ -7034,7 +7153,7 @@
const hal::HWDisplayId hwcId =
(Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId());
- onComposerHalHotplug(hwcId, hal::Connection::CONNECTED);
+ onComposerHalHotplugEvent(hwcId, DisplayHotplugEvent::CONNECTED);
return NO_ERROR;
}
// Modify the max number of display frames stored within FrameTimeline
@@ -7213,7 +7332,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) {
@@ -7253,7 +7372,7 @@
// Update the overlay on the main thread to avoid race conditions with
// RefreshRateSelector::getActiveMode
- static_cast<void>(mScheduler->schedule([=] {
+ static_cast<void>(mScheduler->schedule([=, this] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
@@ -7261,15 +7380,14 @@
}
if (!display->isRefreshRateOverlayEnabled()) return;
- const auto desiredActiveMode = display->getDesiredActiveMode();
- const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
- ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId())
-
- : std::nullopt;
+ const auto desiredModeIdOpt =
+ display->getDesiredMode().transform([](const display::DisplayModeRequest& request) {
+ return request.mode.modePtr->getId();
+ });
const bool timerExpired = mKernelIdleTimerEnabled && expired;
- if (display->onKernelTimerChanged(desiredModeId, timerExpired)) {
+ if (display->onKernelTimerChanged(desiredModeIdOpt, timerExpired)) {
mScheduler->scheduleFrame();
}
}));
@@ -7504,6 +7622,12 @@
return;
}
+ if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
+ ALOGE("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
+ invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
+ return;
+ }
+
wp<const DisplayDevice> displayWeak;
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
@@ -7537,8 +7661,7 @@
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, args.dataspace,
- args.useIdentityTransform, args.hintForSeamlessTransition,
- args.captureSecureLayers);
+ args.hintForSeamlessTransition, args.captureSecureLayers);
});
GetLayerSnapshotsFunction getLayerSnapshots;
@@ -7557,7 +7680,7 @@
args.allowProtected, args.grayscale, captureListener);
}
-void SurfaceFlinger::captureDisplay(DisplayId displayId,
+void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
@@ -7576,10 +7699,22 @@
size = display->getLayerStackSpaceRect().getSize();
}
+ size.width *= args.frameScaleX;
+ size.height *= args.frameScaleY;
+
+ // We could query a real value for this but it'll be a long, long time until we support
+ // displays that need upwards of 1GB per buffer so...
+ constexpr auto kMaxTextureSize = 16384;
+ if (size.width <= 0 || size.height <= 0 || size.width >= kMaxTextureSize ||
+ size.height >= kMaxTextureSize) {
+ ALOGE("capture display resolved to invalid size %d x %d", size.width, size.height);
+ invokeScreenCaptureError(BAD_VALUE, captureListener);
+ return;
+ }
+
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size, ui::Dataspace::UNKNOWN,
- false /* useIdentityTransform */,
- false /* hintForSeamlessTransition */,
+ return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace,
+ args.hintForSeamlessTransition,
false /* captureSecureLayers */);
});
@@ -7603,8 +7738,8 @@
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
- ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale, captureListener);
+ captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, args.pixelFormat,
+ kAllowProtected, kGrayscale, captureListener);
}
void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
@@ -7623,8 +7758,11 @@
std::unordered_set<uint32_t> excludeLayerIds;
ui::Dataspace dataspace = args.dataspace;
- // Call this before holding mStateLock to avoid any deadlocking.
- bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
+ if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
+ ALOGE("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
+ invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
+ return;
+ }
{
Mutex::Autolock lock(mStateLock);
@@ -7636,13 +7774,6 @@
return;
}
- if (!canCaptureBlackoutContent &&
- parent->getDrawingState().flags & layer_state_t::eLayerSecure) {
- ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
- invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
- return;
- }
-
Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState());
if (args.sourceCrop.width() <= 0) {
crop.left = 0;
@@ -7682,7 +7813,7 @@
}
bool childrenOnly = args.childrenOnly;
- RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
+ RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> {
ui::Transform layerTransform;
Rect layerBufferSize;
if (mLayerLifecycleManagerEnabled) {
@@ -7785,12 +7916,11 @@
})
.get();
}
-
+ const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_TEXTURE |
- (hasProtectedLayer && allowProtected && supportsProtected
- ? GRALLOC_USAGE_PROTECTED
- : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ (isProtected ? GRALLOC_USAGE_PROTECTED
+ : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
sp<GraphicBuffer> buffer =
getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
static_cast<android_pixel_format>(reqPixelFormat),
@@ -7810,20 +7940,19 @@
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
auto fence = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
- false /* regionSampling */, grayscale, captureListener);
+ false /* regionSampling */, grayscale, isProtected,
+ captureListener);
fence.get();
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
+ bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
-
auto future = mScheduler->schedule(
- [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
+ [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
ScreenCaptureResults captureResults;
std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
@@ -7838,9 +7967,9 @@
ftl::SharedFuture<FenceResult> renderFuture;
renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
- renderFuture = renderScreenImpl(renderArea, getLayerSnapshots, buffer,
- canCaptureBlackoutContent, regionSampling,
- grayscale, captureResults);
+ renderFuture =
+ renderScreenImpl(renderArea, getLayerSnapshots, buffer, regionSampling,
+ grayscale, isProtected, captureResults);
});
if (captureListener) {
@@ -7867,9 +7996,8 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
- ScreenCaptureResults& captureResults) {
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+ bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) {
ATRACE_CALL();
auto layers = getLayerSnapshots();
@@ -7884,14 +8012,6 @@
layerFE->mSnapshot->geomLayerTransform.inverse();
}
- // We allow the system server to take screenshots of secure layers for
- // use in situations like the Screen-rotation animation and place
- // the impetus on WindowManager to not persist them.
- if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
- ALOGW("FB is protected: PERMISSION_DENIED");
- return ftl::yield<FenceResult>(base::unexpected(PERMISSION_DENIED)).share();
- }
-
auto capturedBuffer = buffer;
auto requestedDataspace = renderArea->getReqDataSpace();
@@ -7972,9 +8092,9 @@
};
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
- sdrWhitePointNits, displayBrightnessNits, grayscale, layerFEs = copyLayerFEs(),
- layerStack, regionSampling, renderArea = std::move(renderArea),
- renderIntent]() -> FenceResult {
+ sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
+ layerFEs = copyLayerFEs(), layerStack, regionSampling,
+ renderArea = std::move(renderArea), renderIntent]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
compositionEngine->setRenderEngine(mRenderEngine.get());
@@ -8008,7 +8128,8 @@
.regionSampling = regionSampling,
.treat170mAsSrgb = mTreat170mAsSrgb,
.dimInGammaSpaceForEnhancedScreenshots =
- dimInGammaSpaceForEnhancedScreenshots});
+ dimInGammaSpaceForEnhancedScreenshots,
+ .isProtected = isProtected});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
@@ -8204,18 +8325,19 @@
auto preferredMode = std::move(*preferredModeOpt);
const auto preferredModeId = preferredMode.modePtr->getId();
+ const Fps preferredFps = preferredMode.fps;
ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
- to_string(preferredMode.fps).c_str());
+ to_string(preferredFps).c_str());
if (!selector.isModeAllowed(preferredMode)) {
ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
return INVALID_OPERATION;
}
- setDesiredActiveMode({preferredMode, .emitEvent = true}, force);
+ setDesiredMode({std::move(preferredMode), .emitEvent = true}, force);
// Update the frameRateOverride list as the display render rate might have changed
- if (mScheduler->updateFrameRateOverrides(/*consideredSignals*/ {}, preferredMode.fps)) {
+ if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) {
triggerOnFrameRateOverridesChanged();
}
@@ -8255,7 +8377,7 @@
return BAD_VALUE;
}
- auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -8372,17 +8494,25 @@
return genericLayerMetadataKeyMap;
}
-status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) {
+status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
PhysicalDisplayId displayId = [&]() {
Mutex::Autolock lock(mStateLock);
return getDefaultDisplayDeviceLocked()->getPhysicalId();
}();
- mScheduler->setGameModeRefreshRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
+ mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
return NO_ERROR;
}
+status_t SurfaceFlinger::setGameDefaultFrameRateOverride(uid_t uid, float frameRate) {
+ if (FlagManager::getInstance().game_default_frame_rate()) {
+ mScheduler->setGameDefaultFrameRateForUid(
+ FrameRateOverride{static_cast<uid_t>(uid), frameRate});
+ }
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::updateSmallAreaDetection(
std::vector<std::pair<int32_t, float>>& appIdThresholdMappings) {
mScheduler->updateSmallAreaDetection(appIdThresholdMappings);
@@ -8397,7 +8527,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) {
@@ -8470,7 +8601,8 @@
}
int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const {
- const auto vsyncConfig = mVsyncConfiguration->getConfigsForRefreshRate(refreshRate).late;
+ const auto vsyncConfig =
+ mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late;
const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
@@ -8551,7 +8683,7 @@
const DisplayDevice& activeDisplay) {
ATRACE_CALL();
- // For the first display activated during boot, there is no need to force setDesiredActiveMode,
+ // For the first display activated during boot, there is no need to force setDesiredMode,
// because DM is about to send its policy via setDesiredDisplayModeSpecs.
bool forceApplyPolicy = false;
@@ -8563,7 +8695,7 @@
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+ mScheduler->resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
@@ -8575,9 +8707,9 @@
sActiveDisplayRotationFlags = ui::Transform::toRotationFlags(activeDisplay.getOrientation());
// The policy of the new active/pacesetter display may have changed while it was inactive. In
- // that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
- // either case, the Scheduler's cachedModeChangedParams must be initialized to the newly active
- // mode, and the kernel idle timer of the newly active display must be toggled.
+ // that case, its preferred mode has not been propagated to HWC (via setDesiredMode). In either
+ // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
+ // and the kernel idle timer of the newly active display must be toggled.
applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(),
forceApplyPolicy);
}
@@ -8601,6 +8733,40 @@
return NO_ERROR;
}
+void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel,
+ int32_t maxLevel) {
+ if (!FlagManager::getInstance().connected_display()) {
+ return;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
+ if (!idOpt) {
+ ALOGE("No display found for HDCP level changed event: connected=%d, max=%d for "
+ "display=%" PRIu64,
+ connectedLevel, maxLevel, hwcDisplayId);
+ return;
+ }
+
+ const bool isInternalDisplay =
+ mPhysicalDisplays.get(*idOpt).transform(&PhysicalDisplay::isInternal).value_or(false);
+ if (isInternalDisplay) {
+ ALOGW("Unexpected HDCP level changed for internal display: connected=%d, max=%d for "
+ "display=%" PRIu64,
+ connectedLevel, maxLevel, hwcDisplayId);
+ return;
+ }
+
+ static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() {
+ if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) {
+ Mutex::Autolock lock(mStateLock);
+ display->setSecure(connectedLevel >= 2 /* HDCP_V1 */);
+ }
+ mScheduler->onHdcpLevelsChanged(mAppConnectionHandle, displayId, connectedLevel, maxLevel);
+ }));
+}
+
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId) {
if (bufferData.buffer &&
@@ -8774,9 +8940,9 @@
}
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
auto& legacyLayer = it->second;
sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
@@ -8845,9 +9011,9 @@
}
auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get();
sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name);
layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
@@ -8922,6 +9088,10 @@
.genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(),
.skipRoundCornersWhenProtected =
!getRenderEngine().supportsProtectedContent()};
+ // The layer may not exist if it was just created and a screenshot was requested immediately
+ // after. In this case, the hierarchy will be empty so we will not render any layers.
+ args.rootSnapshot.isSecure = mLayerLifecycleManager.getLayerFromId(rootLayerId) &&
+ mLayerLifecycleManager.isLayerSecure(rootLayerId);
mLayerSnapshotBuilder.update(args);
auto getLayerSnapshotsFn =
@@ -9035,6 +9205,11 @@
setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | eTraversalNeeded);
}
+void SurfaceFlinger::sfdo_forceClientComposition(bool enabled) {
+ mDebugDisableHWC = enabled;
+ scheduleRepaint();
+}
+
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
@@ -9064,7 +9239,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));
}
@@ -9397,13 +9572,14 @@
}
binder::Status SurfaceComposerAIDL::captureDisplayById(
- int64_t displayId, const sp<IScreenCaptureListener>& captureListener) {
+ int64_t displayId, const CaptureArgs& args,
+ const sp<IScreenCaptureListener>& captureListener) {
// status_t status;
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
- mFlinger->captureDisplay(*id, captureListener);
+ mFlinger->captureDisplay(*id, args, captureListener);
} else {
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
}
@@ -9721,13 +9897,25 @@
return binder::Status::ok();
}
-binder::Status SurfaceComposerAIDL::setOverrideFrameRate(int32_t uid, float frameRate) {
+binder::Status SurfaceComposerAIDL::setGameModeFrameRateOverride(int32_t uid, float frameRate) {
status_t status;
const int c_uid = IPCThreadState::self()->getCallingUid();
if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
- status = mFlinger->setOverrideFrameRate(uid, frameRate);
+ status = mFlinger->setGameModeFrameRateOverride(uid, frameRate);
} else {
- ALOGE("setOverrideFrameRate() permission denied for uid: %d", c_uid);
+ ALOGE("setGameModeFrameRateOverride() permission denied for uid: %d", c_uid);
+ status = PERMISSION_DENIED;
+ }
+ return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setGameDefaultFrameRateOverride(int32_t uid, float frameRate) {
+ status_t status;
+ const int c_uid = IPCThreadState::self()->getCallingUid();
+ if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
+ status = mFlinger->setGameDefaultFrameRateOverride(uid, frameRate);
+ } else {
+ ALOGE("setGameDefaultFrameRateOverride() permission denied for uid: %d", c_uid);
status = PERMISSION_DENIED;
}
return binderStatusFromStatusT(status);
@@ -9753,6 +9941,11 @@
return binder::Status::ok();
}
+binder::Status SurfaceComposerAIDL::forceClientComposition(bool enabled) {
+ mFlinger->sfdo_forceClientComposition(enabled);
+ return binder::Status::ok();
+}
+
binder::Status SurfaceComposerAIDL::updateSmallAreaDetection(const std::vector<int32_t>& appIds,
const std::vector<float>& thresholds) {
status_t status;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 96b67b8..5846214 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -21,6 +21,8 @@
* NOTE: Make sure this file doesn't include anything from <gl/ > or <gl2/ >
*/
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
#include <android/gui/BnSurfaceComposer.h>
#include <android/gui/DisplayStatInfo.h>
@@ -63,13 +65,13 @@
#include <scheduler/interface/ICompositor.h>
#include <ui/FenceResult.h>
+#include <common/FlagManager.h>
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
-#include "FlagManager.h"
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerLifecycleManager.h"
@@ -77,9 +79,9 @@
#include "FrontEnd/LayerSnapshotBuilder.h"
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
+#include "MutexUtils.h"
#include "Scheduler/ISchedulerCallback.h"
#include "Scheduler/RefreshRateSelector.h"
-#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "SurfaceFlingerFactory.h"
#include "ThreadContext.h"
@@ -106,6 +108,7 @@
#include <vector>
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
#include "Client.h"
@@ -130,6 +133,7 @@
class ScreenCapturer;
class WindowInfosListenerInvoker;
+using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using frontend::TransactionHandler;
using gui::CaptureArgs;
@@ -195,7 +199,8 @@
private HWC2::ComposerCallback,
private ICompositor,
private scheduler::ISchedulerCallback,
- private compositionengine::ICEPowerCallback {
+ private compositionengine::ICEPowerCallback,
+ private scheduler::IVsyncTrackerCallback {
public:
struct SkipInitializationTag {};
@@ -475,22 +480,46 @@
return std::bind(std::forward<F>(dump), _3);
}
+ Dumper lockedDumper(Dumper dump) {
+ return [this, dump](const DumpArgs& args, bool asProto, std::string& result) -> void {
+ TimedLock lock(mStateLock, s2ns(1), __func__);
+ if (!lock.locked()) {
+ base::StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+ strerror(-lock.status), lock.status);
+ }
+ dump(args, asProto, result);
+ };
+ }
+
template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
Dumper dumper(F dump) {
using namespace std::placeholders;
- return std::bind(dump, this, _3);
+ return lockedDumper(std::bind(dump, this, _3));
}
template <typename F>
Dumper argsDumper(F dump) {
using namespace std::placeholders;
- return std::bind(dump, this, _1, _3);
+ return lockedDumper(std::bind(dump, this, _1, _3));
}
template <typename F>
Dumper protoDumper(F dump) {
using namespace std::placeholders;
- return std::bind(dump, this, _1, _2, _3);
+ return lockedDumper(std::bind(dump, this, _1, _2, _3));
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper mainThreadDumper(F dump) {
+ using namespace std::placeholders;
+ Dumper dumper = std::bind(dump, this, _3);
+ return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void {
+ mScheduler
+ ->schedule(
+ [&args, asProto, &result, dumper]() FTL_FAKE_GUARD(kMainThreadContext)
+ FTL_FAKE_GUARD(mStateLock) { dumper(args, asProto, result); })
+ .get();
+ };
}
// Maximum allowed number of display frames that can be set through backdoor
@@ -530,7 +559,7 @@
const sp<IBinder>& layerHandle = nullptr);
void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
- void captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ void captureDisplay(DisplayId, const CaptureArgs&, const sp<IScreenCaptureListener>&);
void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
@@ -604,7 +633,9 @@
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const gui::FrameTimelineInfo& frameTimelineInfo);
- status_t setOverrideFrameRate(uid_t uid, float frameRate);
+ status_t setGameModeFrameRateOverride(uid_t uid, float frameRate);
+
+ status_t setGameDefaultFrameRateOverride(uid_t uid, float frameRate);
status_t updateSmallAreaDetection(std::vector<std::pair<int32_t, float>>& uidThresholdMappings);
@@ -622,13 +653,15 @@
status_t getStalledTransactionInfo(
int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result);
+ void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
+
// Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
// HWC2::ComposerCallback overrides:
void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
std::optional<hal::VsyncPeriodNanos>) override;
- void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
+ void onComposerHalHotplugEvent(hal::HWDisplayId, DisplayHotplugEvent) override;
void onComposerHalRefresh(hal::HWDisplayId) override;
void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
const hal::VsyncPeriodChangeTimeline&) override;
@@ -656,6 +689,10 @@
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
+ // IVsyncTrackerCallback overrides
+ void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
+ Fps renderRate) override;
+
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer() REQUIRES(mStateLock);
@@ -681,8 +718,7 @@
// Show hdr sdr ratio overlay
bool mHdrSdrRatioOverlay = false;
- void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
- REQUIRES(mStateLock);
+ void setDesiredMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
Fps maxFps);
@@ -690,9 +726,10 @@
void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
- void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
- // Called when active mode is no longer is progress
- void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+ // TODO(b/241285191): Replace DisplayDevice with DisplayModeRequest, and move to Scheduler.
+ void dropModeRequest(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+ void applyActiveMode(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+
// Called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
REQUIRES(mStateLock, kMainThreadContext);
@@ -747,9 +784,6 @@
void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
- void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext);
- void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
-
/*
* Transactions
*/
@@ -848,11 +882,11 @@
ftl::SharedFuture<FenceResult> captureScreenCommon(
RenderAreaFuture, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, const sp<IScreenCaptureListener>&);
+ bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> renderScreenImpl(
std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
- const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
- bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock)
+ const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
+ bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock)
REQUIRES(kMainThreadContext);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
@@ -982,8 +1016,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
@@ -1073,8 +1107,8 @@
/*
* Debugging & dumpsys
*/
- void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
- std::string& result) const REQUIRES(mStateLock);
+ void dumpAll(const DumpArgs& args, const std::string& compositionLayers,
+ std::string& result) const EXCLUDES(mStateLock);
void dumpHwcLayersMinidump(std::string& result) const REQUIRES(mStateLock, kMainThreadContext);
void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
@@ -1096,7 +1130,8 @@
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
void dumpHdrInfo(std::string& result) const REQUIRES(mStateLock);
- void dumpFrontEnd(std::string& result);
+ void dumpFrontEnd(std::string& result) REQUIRES(kMainThreadContext);
+ void dumpVisibleFrontEnd(std::string& result) REQUIRES(mStateLock, kMainThreadContext);
perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
@@ -1239,6 +1274,7 @@
hal::Connection connection = hal::Connection::INVALID;
};
+ bool mIsHdcpViaNegVsync = false;
bool mIsHotplugErrViaNegVsync = false;
std::mutex mHotplugMutex;
@@ -1334,10 +1370,6 @@
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;
- // Stores phase offsets configured per refresh rate.
- std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
-
- std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
bool mLumaSampling = true;
@@ -1421,8 +1453,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);
@@ -1434,7 +1464,7 @@
bool mLegacyFrontEndEnabled = true;
frontend::LayerLifecycleManager mLayerLifecycleManager;
- frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
+ frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles;
@@ -1455,16 +1485,24 @@
// Map of displayid to mirrorRoot
ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
+ // NotifyExpectedPresentHint
+ struct NotifyExpectedPresentData {
+ // lastExpectedPresentTimestamp is read and write from multiple threads such as
+ // main thread, EventThread, MessageQueue. And is atomic for that reason.
+ std::atomic<TimePoint> lastExpectedPresentTimestamp{};
+ Fps lastFrameInterval{};
+ };
+ std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap;
+
+ void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt);
+
void sfdo_enableRefreshRateOverlay(bool active);
void sfdo_setDebugFlash(int delay);
void sfdo_scheduleComposite();
void sfdo_scheduleCommit();
-
- // Trunk-Stable flags
- bool mMiscFlagValue;
- bool mConnectedDisplayFlagValue;
- bool mMisc2FlagEarlyBootValue;
- bool mMisc2FlagLateBootValue;
+ void sfdo_forceClientComposition(bool enabled);
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1510,7 +1548,8 @@
binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
binder::Status captureDisplay(const DisplayCaptureArgs&,
const sp<IScreenCaptureListener>&) override;
- binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override;
+ binder::Status captureDisplayById(int64_t, const CaptureArgs&,
+ const sp<IScreenCaptureListener>&) override;
binder::Status captureLayers(const LayerCaptureArgs&,
const sp<IScreenCaptureListener>&) override;
@@ -1570,11 +1609,13 @@
binder::Status getDisplayDecorationSupport(
const sp<IBinder>& displayToken,
std::optional<gui::DisplayDecorationSupport>* outSupport) override;
- binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
+ binder::Status setGameModeFrameRateOverride(int32_t uid, float frameRate) override;
+ binder::Status setGameDefaultFrameRateOverride(int32_t uid, float frameRate) override;
binder::Status enableRefreshRateOverlay(bool active) override;
binder::Status setDebugFlash(int delay) override;
binder::Status scheduleComposite() override;
binder::Status scheduleCommit() override;
+ binder::Status forceClientComposition(bool enabled) override;
binder::Status updateSmallAreaDetection(const std::vector<int32_t>& appIds,
const std::vector<float>& thresholds) override;
binder::Status setSmallAreaDetectionThreshold(int32_t appId, float threshold) override;
diff --git a/services/surfaceflinger/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/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 9d6d87e..696f348 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -16,12 +16,10 @@
#undef LOG_TAG
#define LOG_TAG "TransactionTracing"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <android-base/stringprintf.h>
#include <log/log.h>
#include <utils/SystemClock.h>
-#include <utils/Trace.h>
#include "Client.h"
#include "FrontEnd/LayerCreationArgs.h"
@@ -230,7 +228,6 @@
void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates,
const std::vector<uint32_t>& destroyedLayers) {
- ATRACE_CALL();
std::scoped_lock lock(mTraceLock);
std::vector<std::string> removedEntries;
perfetto::protos::TransactionTraceEntry entryProto;
@@ -441,6 +438,7 @@
for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
entryProto.mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
}
+ entryProto.set_displays_changed(true);
return entryProto;
}
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index ddbf3e4..6a66fff 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -218,6 +218,13 @@
friend class Singleton<TransactionTracing>;
std::function<void(const std::string& prefix, bool overwrite)> mWriterFunction =
[](const std::string&, bool) {};
+ std::atomic<bool> mEnabled{true};
+
+ void doInvoke(const std::string& filename, bool overwrite) {
+ if (mEnabled) {
+ mWriterFunction(filename, overwrite);
+ }
+ };
public:
void setWriterFunction(
@@ -225,12 +232,15 @@
mWriterFunction = std::move(function);
}
void invoke(const std::string& prefix, bool overwrite) {
- mWriterFunction(TransactionTracing::getFilePath(prefix), overwrite);
+ doInvoke(TransactionTracing::getFilePath(prefix), overwrite);
}
/* pass in a complete file path for testing */
void invokeForTest(const std::string& filename, bool overwrite) {
- mWriterFunction(filename, overwrite);
+ doInvoke(filename, overwrite);
}
+ /* hacky way to avoid generating traces when converting transaction trace to layers trace. */
+ void disable() { mEnabled.store(false); }
+ void enable() { mEnabled.store(true); }
};
} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index c2d1954..617ea2c 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -40,9 +40,24 @@
namespace android {
using namespace ftl::flag_operators;
+namespace {
+class ScopedTraceDisabler {
+public:
+ ScopedTraceDisabler() { TransactionTraceWriter::getInstance().disable(); }
+ ~ScopedTraceDisabler() { TransactionTraceWriter::getInstance().enable(); }
+};
+} // namespace
+
bool LayerTraceGenerator::generate(const perfetto::protos::TransactionTraceFile& traceFile,
std::uint32_t traceFlags, LayerTracing& layerTracing,
bool onlyLastEntry) {
+ // We are generating the layers trace by replaying back a set of transactions. If the
+ // transactions have unexpected states, we may generate a transaction trace to debug
+ // the unexpected state. This is silly. So we disable it by poking the
+ // TransactionTraceWriter. This is really a hack since we should manage our depenecies a
+ // little better.
+ ScopedTraceDisabler fatalErrorTraceDisabler;
+
if (traceFile.entry_size() == 0) {
ALOGD("Trace file is empty");
return false;
@@ -52,7 +67,7 @@
// frontend
frontend::LayerLifecycleManager lifecycleManager;
- frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
+ frontend::LayerHierarchyBuilder hierarchyBuilder;
frontend::LayerSnapshotBuilder snapshotBuilder;
ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
@@ -119,12 +134,10 @@
lifecycleManager.applyTransactions(transactions, /*ignoreUnknownHandles=*/true);
lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true);
- if (lifecycleManager.getGlobalChanges().test(
- frontend::RequestedLayerState::Changes::Hierarchy)) {
- hierarchyBuilder.update(lifecycleManager.getLayers(),
- lifecycleManager.getDestroyedLayers());
- }
+ // update hierarchy
+ hierarchyBuilder.update(lifecycleManager);
+ // update snapshots
frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
.layerLifecycleManager = lifecycleManager,
.displays = displayInfos,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 3587a72..6a155c1 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -158,7 +158,7 @@
handle->previousReleaseFence = prevFence;
handle->previousReleaseFences.clear();
- FrameEventHistoryStats eventStats(handle->frameNumber,
+ FrameEventHistoryStats eventStats(handle->frameNumber, handle->previousFrameNumber,
handle->gpuCompositionDoneFence->getSnapshot().fence,
handle->compositorTiming, handle->refreshStartTime,
handle->dequeueReadyTime);
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 3074795..245398f 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -56,6 +56,7 @@
nsecs_t refreshStartTime = 0;
nsecs_t dequeueReadyTime = 0;
uint64_t frameNumber = 0;
+ uint64_t previousFrameNumber = 0;
ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
};
diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h
index ee94217..62d2ebb 100644
--- a/services/surfaceflinger/Utils/Dumper.h
+++ b/services/surfaceflinger/Utils/Dumper.h
@@ -35,6 +35,8 @@
void eol() { mOut += '\n'; }
+ std::string& out() { return mOut; }
+
void dump(std::string_view name, std::string_view value = {}) {
using namespace std::string_view_literals;
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/common/Android.bp b/services/surfaceflinger/common/Android.bp
new file mode 100644
index 0000000..5ef22b5
--- /dev/null
+++ b/services/surfaceflinger/common/Android.bp
@@ -0,0 +1,48 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_defaults",
+ defaults: [
+ "android.hardware.graphics.composer3-ndk_shared",
+ "surfaceflinger_defaults",
+ ],
+ shared_libs: [
+ "libSurfaceFlingerProp",
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "librenderengine",
+ ],
+ srcs: [
+ "FlagManager.cpp",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libsurfaceflinger_common",
+ defaults: [
+ "libsurfaceflinger_common_defaults",
+ ],
+ static_libs: [
+ "libsurfaceflingerflags",
+ ],
+}
+
+cc_library_static {
+ name: "libsurfaceflinger_common_test",
+ defaults: [
+ "libsurfaceflinger_common_defaults",
+ ],
+ static_libs: [
+ "libsurfaceflingerflags_test",
+ ],
+}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
new file mode 100644
index 0000000..a27e100
--- /dev/null
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <common/FlagManager.h>
+
+#include <SurfaceFlingerProperties.sysprop.h>
+#include <android-base/parsebool.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <renderengine/RenderEngine.h>
+#include <server_configurable_flags/get_flags.h>
+#include <cinttypes>
+
+#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;
+
+namespace {
+std::optional<bool> parseBool(const char* str) {
+ base::ParseBoolResult parseResult = base::ParseBool(str);
+ switch (parseResult) {
+ case base::ParseBoolResult::kTrue:
+ return std::make_optional(true);
+ case base::ParseBoolResult::kFalse:
+ return std::make_optional(false);
+ case base::ParseBoolResult::kError:
+ return std::nullopt;
+ }
+}
+
+bool getFlagValue(std::function<bool()> getter, std::optional<bool> overrideValue) {
+ if (overrideValue.has_value()) {
+ return *overrideValue;
+ }
+
+ return getter();
+}
+
+} // namespace
+
+const FlagManager& FlagManager::getInstance() {
+ return getMutableInstance();
+}
+
+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);
+ }
+}
+
+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);
+ DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace);
+ DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency);
+ DUMP_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved);
+ DUMP_READ_ONLY_FLAG(enable_fro_dependent_features);
+ DUMP_READ_ONLY_FLAG(display_protected);
+ DUMP_READ_ONLY_FLAG(fp16_client_target);
+ DUMP_READ_ONLY_FLAG(game_default_frame_rate);
+ DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
+#undef DUMP_READ_ONLY_FLAG
+#undef DUMP_SERVER_FLAG
+#undef DUMP_FLAG_INTERVAL
+}
+
+std::optional<bool> FlagManager::getBoolProperty(const char* property) const {
+ return parseBool(base::GetProperty(property, "").c_str());
+}
+
+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 const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
+ static const bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ if (mUnitTestMode) { \
+ /* \
+ * When testing, we don't want to rely on the cached `value` or the debugOverride. \
+ */ \
+ return flags::name(); \
+ } \
+ 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")
+FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "")
+FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "")
+FLAG_MANAGER_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved,
+ "debug.sf.cache_source_crop_only_moved")
+FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "")
+FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "")
+FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
+FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
+FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
+
+/// 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/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
new file mode 100644
index 0000000..2f2895c
--- /dev/null
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#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. Can be called from any thread.
+class FlagManager {
+private:
+ // Effectively making the constructor private, while allowing std::make_unique to work
+ struct ConstructorTag {};
+
+public:
+ static const FlagManager& getInstance();
+ static FlagManager& getMutableInstance();
+
+ FlagManager(ConstructorTag);
+ virtual ~FlagManager();
+
+ void markBootCompleted();
+ void dump(std::string& result) 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;
+ bool add_sf_skipped_frames_to_trace() const;
+ bool use_known_refresh_rate_for_fps_consistency() const;
+ bool cache_when_source_crop_layer_only_moved() const;
+ bool enable_fro_dependent_features() const;
+ bool display_protected() const;
+ bool fp16_client_target() const;
+ bool game_default_frame_rate() const;
+ bool enable_layer_command_batching() const;
+
+protected:
+ // overridden for unit tests
+ virtual std::optional<bool> getBoolProperty(const char*) const;
+ virtual bool getServerConfigurableFlag(const char*) const;
+
+private:
+ friend class TestableFlagManager;
+
+ FlagManager(const FlagManager&) = delete;
+
+ void dumpFlag(std::string& result, bool readonly, const char* name,
+ std::function<bool()> getter) const;
+
+ std::atomic_bool mBootCompleted = false;
+ std::atomic_bool mUnitTestMode = false;
+
+ static std::unique_ptr<FlagManager> mInstance;
+ static std::once_flag mOnce;
+};
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/common/include/common/test/FlagUtils.h
similarity index 91%
rename from services/surfaceflinger/tests/unittests/FlagUtils.h
rename to services/surfaceflinger/common/include/common/test/FlagUtils.h
index 7103684..550c70d 100644
--- a/services/surfaceflinger/tests/unittests/FlagUtils.h
+++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h
@@ -16,12 +16,16 @@
#pragma once
+#include <common/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/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index 0f9060d..ab3b352 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",
@@ -38,6 +39,7 @@
"libgtest_ndk_c++",
"libgmock_main_ndk",
"librenderengine_mocks",
+ "libsurfaceflinger_common",
"perfetto_trace_protos",
"libcompositionengine_mocks",
"perfetto_trace_protos",
@@ -73,9 +75,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..68237c8 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) {
@@ -277,7 +286,7 @@
composer.setClientTarget(display, mFdp.ConsumeIntegral<uint32_t>(), sp<GraphicBuffer>(),
mFdp.ConsumeIntegral<int32_t>(), mFdp.PickValueInArray(kDataspaces),
- {});
+ {}, mFdp.ConsumeFloatingPoint<float>());
composer.setColorMode(display, mFdp.PickValueInArray(kColormodes),
mFdp.PickValueInArray(kRenderIntents));
@@ -485,7 +494,7 @@
surface->beginFrame(mFdp.ConsumeBool());
surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes));
- surface->advanceFrame();
+ surface->advanceFrame(mFdp.ConsumeFloatingPoint<float>());
surface->onFrameCommitted();
String8 result = String8(mFdp.ConsumeRandomLengthString().c_str());
surface->dumpAsString(result);
@@ -521,7 +530,7 @@
surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes));
surface->resizeBuffers(getFuzzedSize());
surface->getClientTargetAcquireFence();
- surface->advanceFrame();
+ surface->advanceFrame(mFdp.ConsumeFloatingPoint<float>());
surface->onFrameCommitted();
String8 result = String8(mFdp.ConsumeRandomLengthString().c_str());
surface->dumpAsString(result);
@@ -552,7 +561,8 @@
getDeviceCompositionChanges(halDisplayID);
mHwc.setClientTarget(halDisplayID, mFdp.ConsumeIntegral<uint32_t>(), Fence::NO_FENCE,
- sp<GraphicBuffer>::make(), mFdp.PickValueInArray(kDataspaces));
+ sp<GraphicBuffer>::make(), mFdp.PickValueInArray(kDataspaces),
+ mFdp.ConsumeFloatingPoint<float>());
mHwc.presentAndGetReleaseFences(halDisplayID, std::chrono::steady_clock::now());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
index 1a951b3..b2dc20e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
@@ -41,6 +41,7 @@
namespace android::hardware::graphics::composer::hal {
+using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -52,7 +53,9 @@
: mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
Return<void> onHotplug(HWDisplayId display, Connection connection) override {
- mCallback->onComposerHalHotplug(display, connection);
+ const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED
+ : DisplayHotplugEvent::DISCONNECTED;
+ mCallback->onComposerHalHotplugEvent(display, event);
return Void();
}
@@ -94,7 +97,7 @@
struct TestHWC2ComposerCallback : public HWC2::ComposerCallback {
virtual ~TestHWC2ComposerCallback() = default;
- void onComposerHalHotplug(HWDisplayId, Connection){};
+ void onComposerHalHotplugEvent(HWDisplayId, DisplayHotplugEvent) {}
void onComposerHalRefresh(HWDisplayId) {}
void onComposerHalVsync(HWDisplayId, int64_t, std::optional<VsyncPeriodNanos>) {}
void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {}
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..fa79956 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -224,17 +224,13 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
- : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
- std::make_shared<android::mock::VSyncTracker>(), selectorPtr,
- std::move(modulatorPtr), callback) {}
-
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)) {
+ surfaceflinger::Factory& factory, TimeStats& timeStats,
+ ISchedulerCallback& callback, IVsyncTrackerCallback& vsyncTrackerCallback)
+ : Scheduler(*this, callback, Feature::kContentDetection, factory,
+ selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplayInternal(displayId, std::move(selectorPtr),
std::shared_ptr<VsyncSchedule>(
@@ -400,7 +396,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;
@@ -609,9 +606,16 @@
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool());
- mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter),
- mFdp.ConsumeIntegral<nsecs_t>());
+ scheduler::FeatureFlags flags;
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kBackpressureGpuComposition;
+ }
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kExpectedPresentTime;
+ }
+ scheduler::FrameTargeter frameTargeter(displayId, flags);
+ 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));
@@ -666,19 +671,12 @@
}
mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
- const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getVsyncRate();
- mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-
- mFlinger->mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
- hal::PowerMode::OFF);
-
- auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
- mFlinger->mVsyncConfiguration->getCurrentConfigs());
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker), mRefreshRateSelector,
- std::move(modulatorPtr), *(callback ?: this));
+ mFactory, *mFlinger->mTimeStats,
+ *(callback ?: this),
+ *(vsyncTrackerCallback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -799,6 +797,9 @@
void triggerOnFrameRateOverridesChanged() override {}
void onChoreographerAttached() override {}
+ // IVsyncTrackerCallback overrides
+ void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) 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..8a5500f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -24,10 +24,12 @@
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/RefreshRateSelector.h"
+#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/VSyncDispatchTimerQueue.h"
#include "Scheduler/VSyncPredictor.h"
#include "Scheduler/VSyncReactor.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
@@ -133,13 +135,13 @@
dispatch->schedule(tmp,
{.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()});
},
"o.o");
dispatch->schedule(tmp,
{.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()});
dispatch->unregisterCallback(tmp);
dispatch->cancel(tmp);
}
@@ -161,33 +163,41 @@
entry.update(*stubTracker, 0);
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()},
*stubTracker, 0);
entry.disarm();
entry.ensureNotRunning();
entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()},
*stubTracker, 0);
auto const wakeup = entry.wakeupTime();
auto const ready = entry.readyTime();
entry.callback(entry.executing(), *wakeup, *ready);
entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
.readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
- .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()});
+ .lastVsync = mFdp.ConsumeIntegral<nsecs_t>()});
dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp);
}
+struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback {
+ void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) 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,
- minimumSamplesForPrediction,
- mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
+ VsyncTrackerCallback callback;
+ const auto mode = ftl::as_non_null(
+ mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod)));
+ scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction,
+ mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/,
+ callback};
uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
- tracker.setPeriod(period);
+ tracker.setDisplayModePtr(ftl::as_non_null(
+ mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period))));
for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) {
if (!tracker.needsMoreSamples()) {
break;
@@ -262,7 +272,10 @@
*vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
false);
- reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool());
+ const auto mode = ftl::as_non_null(
+ mock::createDisplayMode(DisplayModeId(0),
+ Fps::fromPeriodNsecs(mFdp.ConsumeIntegral<nsecs_t>())));
+ reactor.onDisplayModeChanged(mode, mFdp.ConsumeBool());
bool periodFlushed = false; // Value does not matter, since this is an out
// param from addHwVsyncTimestamp.
reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
@@ -413,7 +426,15 @@
}
void SchedulerFuzzer::fuzzFrameTargeter() {
- scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool());
+ scheduler::FeatureFlags flags;
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kBackpressureGpuComposition;
+ }
+ if (mFdp.ConsumeBool()) {
+ flags |= scheduler::Feature::kExpectedPresentTime;
+ }
+
+ scheduler::FrameTargeter frameTargeter(kDisplayId, flags);
const struct VsyncSource final : scheduler::IVsyncSource {
explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
@@ -421,6 +442,7 @@
Period period() const { return getFuzzedDuration(fuzzer); }
TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
+ Period minFramePeriod() const { return period(); }
} vsyncSource{mFdp};
int i = 10;
@@ -428,7 +450,8 @@
frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
.vsyncId = getFuzzedVsyncId(mFdp),
.expectedVsyncTime = getFuzzedTimePoint(mFdp),
- .sfWorkDuration = getFuzzedDuration(mFdp)},
+ .sfWorkDuration = getFuzzedDuration(mFdp),
+ .hwcMinWorkDuration = getFuzzedDuration(mFdp)},
vsyncSource);
frameTargeter.setPresentFence(makeFakeFence());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 8061a8f..114f3b0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -86,11 +86,13 @@
bool addVsyncTimestamp(nsecs_t /* timestamp */) override { return true; }
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */) const override { return 1; }
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */,
+ std::optional<nsecs_t>) const override {
+ return 1;
+ }
nsecs_t currentPeriod() const override { return 1; }
-
- void setPeriod(nsecs_t /* period */) override {}
+ Period minFramePeriod() const override { return Period::fromNs(currentPeriod()); }
void resetModel() override {}
@@ -100,7 +102,7 @@
return true;
}
- void setRenderRate(Fps) override {}
+ void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) override {}
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
@@ -109,6 +111,12 @@
return (timePoint - (timePoint % mPeriod) + mPeriod);
}
+ void setRenderRate(Fps) override {}
+
+ void onFrameBegin(TimePoint, TimePoint) override {}
+
+ void onFrameMissed(TimePoint) override {}
+
void dump(std::string& /* result */) const override {}
protected:
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 9599452..6c8972f 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -29,6 +29,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
+#include <common/FlagManager.h>
#include <configstore/Utils.h>
#include <displayservice/DisplayService.h>
#include <errno.h>
@@ -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..79379fe 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -32,6 +32,14 @@
}
flag {
+ name: "enable_layer_command_batching"
+ namespace: "core_graphics"
+ description: "This flag controls batching on createLayer/destroyLayer command with executeCommand."
+ bug: "290685621"
+ is_fixed_read_only: true
+}
+
+flag {
name: "dont_skip_on_early"
namespace: "core_graphics"
description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
@@ -52,4 +60,93 @@
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: "add_sf_skipped_frames_to_trace"
+ namespace: "core_graphics"
+ description: "Add SurfaceFlinger dropped Frames to frame timeline"
+ bug: "273701290"
+ 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"
+}
+
+flag {
+ name: "use_known_refresh_rate_for_fps_consistency"
+ namespace: "core_graphics"
+ description: "Whether to use the closest known refresh rate to determine the fps consistency."
+ bug: "299201319"
+ is_fixed_read_only: true
+}
+
+# This flag is broken.
+# See alternative one: cache_when_source_crop_layer_only_moved
+# flag {
+# name: "cache_if_source_crop_layer_only_moved"
+# namespace: "core_graphics"
+# description: "do not flatten layers if source crop is only moved"
+# bug: "305718400"
+# is_fixed_read_only: true
+# }
+
+flag {
+ name: "cache_when_source_crop_layer_only_moved"
+ namespace: "core_graphics"
+ description: "do not flatten layers if source crop is only moved"
+ bug: "305718400"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_fro_dependent_features"
+ namespace: "core_graphics"
+ description: "enable frame rate override dependent features by default"
+ bug: "314217419"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "display_protected"
+ namespace: "core_graphics"
+ description: "Introduce protected displays to specify whether they should render protected content"
+ bug: "301647974"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "fp16_client_target"
+ namespace: "core_graphics"
+ description: "Controls whether we render to fp16 client targets"
+ bug: "236745178"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "game_default_frame_rate"
+ namespace: "game"
+ description: "This flag guards the new behavior with the addition of Game Default Frame Rate feature."
+ bug: "286084594"
+ is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 5888a55..5449aeb 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -55,7 +55,6 @@
"ReleaseBufferCallback_test.cpp",
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
- "SetFrameRateOverride_test.cpp",
"SetGeometry_test.cpp",
"Stress_test.cpp",
"TextureFiltering_test.cpp",
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 0a951d4..aeff5a5 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -56,60 +56,44 @@
ASSERT_EQ(res, NO_ERROR);
}
- void testSetAllowGroupSwitching(bool allowGroupSwitching);
+ void testSetDesiredDisplayModeSpecs(bool allowGroupSwitching = false) {
+ ui::DynamicDisplayInfo info;
+ status_t res =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+ &info);
+ const auto& modes = info.supportedDisplayModes;
+ ASSERT_EQ(res, NO_ERROR);
+ ASSERT_GT(modes.size(), 0);
+ for (const auto& mode : modes) {
+ gui::DisplayModeSpecs setSpecs;
+ setSpecs.defaultMode = mode.id;
+ setSpecs.allowGroupSwitching = allowGroupSwitching;
+ setSpecs.primaryRanges.physical.min = mode.peakRefreshRate;
+ setSpecs.primaryRanges.physical.max = mode.peakRefreshRate;
+ setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+ setSpecs.appRequestRanges = setSpecs.primaryRanges;
+
+ res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
+ ASSERT_EQ(res, NO_ERROR);
+ gui::DisplayModeSpecs getSpecs;
+ res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
+ ASSERT_EQ(res, NO_ERROR);
+ ASSERT_EQ(setSpecs, getSpecs);
+ }
+ }
sp<IBinder> mDisplayToken;
uint64_t mDisplayId;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
- ui::DynamicDisplayInfo info;
- status_t res =
- SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
- &info);
- const auto& modes = info.supportedDisplayModes;
- ASSERT_EQ(res, NO_ERROR);
- ASSERT_GT(modes.size(), 0);
-
- gui::DisplayModeSpecs setSpecs;
- setSpecs.allowGroupSwitching = false;
- for (size_t i = 0; i < modes.size(); i++) {
- setSpecs.defaultMode = modes[i].id;
- setSpecs.primaryRanges.physical.min = modes[i].peakRefreshRate;
- setSpecs.primaryRanges.physical.max = modes[i].peakRefreshRate;
- setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
- setSpecs.appRequestRanges = setSpecs.primaryRanges;
- res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
- ASSERT_EQ(res, NO_ERROR);
-
- gui::DisplayModeSpecs getSpecs;
- res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
- ASSERT_EQ(res, NO_ERROR);
- ASSERT_EQ(setSpecs, getSpecs);
- }
-}
-
-void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
- gui::DisplayModeSpecs setSpecs;
- setSpecs.defaultMode = 0;
- setSpecs.allowGroupSwitching = allowGroupSwitching;
- setSpecs.primaryRanges.physical.min = 0;
- setSpecs.primaryRanges.physical.max = 90;
- setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
- setSpecs.appRequestRanges = setSpecs.primaryRanges;
-
- status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
- ASSERT_EQ(res, NO_ERROR);
- gui::DisplayModeSpecs getSpecs;
- res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
- ASSERT_EQ(res, NO_ERROR);
- ASSERT_EQ(setSpecs, getSpecs);
+ testSetDesiredDisplayModeSpecs();
}
TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
- testSetAllowGroupSwitching(true);
- testSetAllowGroupSwitching(false);
- testSetAllowGroupSwitching(true);
+ testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/true);
+ testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/false);
+ testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/true);
}
} // namespace android
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 2181370..15a98df 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -38,7 +38,6 @@
args.displayToken = sp<BBinder>::make();
args.width = 10;
args.height = 20;
- args.useIdentityTransform = true;
args.grayscale = true;
Parcel p;
@@ -56,7 +55,6 @@
ASSERT_EQ(args.displayToken, args2.displayToken);
ASSERT_EQ(args.width, args2.width);
ASSERT_EQ(args.height, args2.height);
- ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform);
ASSERT_EQ(args.grayscale, args2.grayscale);
}
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..18262f6 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,30 +96,45 @@
{
// 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));
-
{
- SCOPED_TRACE("as system");
- auto shot = screenshot();
- shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ 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::captureLayers(mCaptureArgs, mCaptureResults));
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ mCaptureArgs.captureSecureLayers = true;
+ // AID_SYSTEM is allowed to capture secure content.
+ 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);
}
- // 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));
- ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
- ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
- sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+ {
+ // Attempt secure screenshot from shell since it doesn't have CAPTURE_BLACKOUT_CONTENT
+ // permission, but is allowed normal screenshots.
+ UIDFaker faker(AID_SHELL);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
+ }
+
+ // Remove flag secure from the layer.
+ Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
+ {
+ // Assert that screenshot fails without CAPTURE_BLACKOUT_CONTENT when requesting
+ // captureSecureLayers even if there are no actual secure layers on screen.
+ UIDFaker faker(AID_SHELL);
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
+ }
}
TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) {
@@ -131,7 +142,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,15 +164,140 @@
// 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);
}
+/**
+ * If a parent layer sets the secure flag, but the screenshot requests is for the child hierarchy,
+ * we need to ensure the secure flag is respected from the parent even though the parent isn't
+ * in the captured sub-hierarchy
+ */
+TEST_F(ScreenCaptureTest, CaptureChildRespectsParentSecureFlag) {
+ Rect size(0, 0, 100, 100);
+ Transaction().hide(mBGSurfaceControl).hide(mFGSurfaceControl).apply();
+ sp<SurfaceControl> parentLayer;
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent-test", 0, 0,
+ ISurfaceComposerClient::eHidden,
+ mRootSurfaceControl.get()));
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("child-test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ parentLayer.get()));
+ ASSERT_NO_FATAL_FAILURE(
+ fillBufferLayerColor(childLayer, Color::GREEN, size.width(), size.height()));
+
+ // hide the parent layer to ensure secure flag is passed down to child when screenshotting
+ Transaction().setLayer(parentLayer, INT32_MAX).show(childLayer).apply(true);
+ Transaction()
+ .setFlags(parentLayer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+ .apply();
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = childLayer->getHandle();
+ captureArgs.sourceCrop = size;
+ captureArgs.captureSecureLayers = false;
+ {
+ SCOPED_TRACE("parent hidden");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::BLACK);
+ }
+
+ captureArgs.captureSecureLayers = true;
+ {
+ SCOPED_TRACE("capture secure parent not visible");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::GREEN);
+ }
+
+ Transaction().show(parentLayer).apply();
+ captureArgs.captureSecureLayers = false;
+ {
+ SCOPED_TRACE("parent visible");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::BLACK);
+ }
+
+ captureArgs.captureSecureLayers = true;
+ {
+ SCOPED_TRACE("capture secure parent visible");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::GREEN);
+ }
+}
+
+TEST_F(ScreenCaptureTest, CaptureOffscreenChildRespectsParentSecureFlag) {
+ Rect size(0, 0, 100, 100);
+ Transaction().hide(mBGSurfaceControl).hide(mFGSurfaceControl).apply();
+ // Parent layer should be offscreen.
+ sp<SurfaceControl> parentLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ parentLayer = createLayer("parent-test", 0, 0, ISurfaceComposerClient::eHidden));
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("child-test", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ parentLayer.get()));
+ ASSERT_NO_FATAL_FAILURE(
+ fillBufferLayerColor(childLayer, Color::GREEN, size.width(), size.height()));
+
+ // hide the parent layer to ensure secure flag is passed down to child when screenshotting
+ Transaction().setLayer(parentLayer, INT32_MAX).show(childLayer).apply(true);
+ Transaction()
+ .setFlags(parentLayer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+ .apply();
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = childLayer->getHandle();
+ captureArgs.sourceCrop = size;
+ captureArgs.captureSecureLayers = false;
+ {
+ SCOPED_TRACE("parent hidden");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::BLACK);
+ }
+
+ captureArgs.captureSecureLayers = true;
+ {
+ SCOPED_TRACE("capture secure parent not visible");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::GREEN);
+ }
+
+ Transaction().show(parentLayer).apply();
+ captureArgs.captureSecureLayers = false;
+ {
+ SCOPED_TRACE("parent visible");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::BLACK);
+ }
+
+ captureArgs.captureSecureLayers = true;
+ {
+ SCOPED_TRACE("capture secure parent visible");
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
+ sc.expectColor(size, Color::GREEN);
+ }
+}
+
TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mBGSurfaceControl->getHandle();
@@ -232,7 +369,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 +742,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 +798,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 +817,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 +842,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 +849,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 +864,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 +985,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 +999,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/SetFrameRateOverride_test.cpp b/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
deleted file mode 100644
index e43ef95..0000000
--- a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
+++ /dev/null
@@ -1,100 +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.
- */
-
-#include <android/gui/ISurfaceComposer.h>
-#include <gtest/gtest.h>
-#include <gui/DisplayEventReceiver.h>
-#include <gui/SurfaceComposerClient.h>
-#include <sys/epoll.h>
-#include <algorithm>
-
-namespace android {
-namespace {
-using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
-using gui::ISurfaceComposer;
-
-class SetFrameRateOverrideTest : public ::testing::Test {
-protected:
- void SetUp() override {
- const ISurfaceComposer::VsyncSource vsyncSource =
- ISurfaceComposer::VsyncSource::eVsyncSourceApp;
- const EventRegistrationFlags eventRegistration = {
- ISurfaceComposer::EventRegistration::frameRateOverride};
-
- mDisplayEventReceiver =
- std::make_unique<DisplayEventReceiver>(vsyncSource, eventRegistration);
- EXPECT_EQ(NO_ERROR, mDisplayEventReceiver->initCheck());
-
- mEpollFd = epoll_create1(EPOLL_CLOEXEC);
- EXPECT_GT(mEpollFd, 1);
-
- epoll_event event;
- event.events = EPOLLIN;
- EXPECT_EQ(0, epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mDisplayEventReceiver->getFd(), &event));
- }
-
- void TearDown() override { close(mEpollFd); }
-
- void setFrameRateAndListenEvents(uid_t uid, float frameRate) {
- status_t ret = SurfaceComposerClient::setOverrideFrameRate(uid, frameRate);
- ASSERT_EQ(NO_ERROR, ret);
-
- DisplayEventReceiver::Event event;
- bool isOverrideFlushReceived = false;
- mFrameRateOverrides.clear();
-
- epoll_event epollEvent;
- while (epoll_wait(mEpollFd, &epollEvent, 1, 1000) > 0) {
- while (mDisplayEventReceiver->getEvents(&event, 1) > 0) {
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
- mFrameRateOverrides.emplace_back(event.frameRateOverride);
- }
- if (event.header.type ==
- DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
- isOverrideFlushReceived = true;
- }
- }
-
- if (isOverrideFlushReceived) break;
- }
- }
-
- std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
- std::vector<FrameRateOverride> mFrameRateOverrides;
-
- int mEpollFd;
-};
-
-TEST_F(SetFrameRateOverrideTest, SetFrameRateOverrideCall) {
- uid_t uid = getuid();
- float frameRate = 30.0f;
- setFrameRateAndListenEvents(uid, frameRate);
- // check if the frame rate override we set exists
- ASSERT_TRUE(std::find_if(mFrameRateOverrides.begin(), mFrameRateOverrides.end(),
- [uid = uid, frameRate = frameRate](auto i) {
- return uid == i.uid && frameRate == i.frameRateHz;
- }) != mFrameRateOverrides.end());
-
- // test removing frame rate override
- frameRate = 0.0f;
- setFrameRateAndListenEvents(uid, frameRate);
- ASSERT_TRUE(std::find_if(mFrameRateOverrides.begin(), mFrameRateOverrides.end(),
- [uid = uid, frameRate = frameRate](auto i) {
- return uid == i.uid && frameRate == i.frameRateHz;
- }) == mFrameRateOverrides.end());
-}
-} // namespace
-} // namespace android
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..5809ea0 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 {
@@ -106,6 +106,7 @@
"SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_InitializeDisplaysTest.cpp",
+ "SurfaceFlinger_NotifyExpectedPresentTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_PowerHintTest.cpp",
"SurfaceFlinger_SetDisplayStateTest.cpp",
@@ -117,6 +118,7 @@
"RefreshRateSelectorTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
+ "TestableScheduler.cpp",
"TimeStatsTest.cpp",
"FrameTracerTest.cpp",
"TransactionApplicationTest.cpp",
@@ -170,6 +172,7 @@
"librenderengine_mocks",
"libscheduler",
"libserviceutils",
+ "libsurfaceflinger_common_test",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 8e13c0d..beb2147 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -194,7 +194,6 @@
LayerCase::setupForScreenCapture(this);
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
- constexpr bool forSystem = true;
constexpr bool regionSampling = false;
auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
@@ -216,7 +215,7 @@
usage);
auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots,
- mCaptureScreenBuffer, forSystem, regionSampling);
+ mCaptureScreenBuffer, regionSampling);
ASSERT_TRUE(future.valid());
const auto fenceResult = future.get();
@@ -316,7 +315,7 @@
EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
- EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
+ EXPECT_CALL(*test->mDisplaySurface, advanceFrame(_)).Times(1);
Case::CompositionType::setupHwcSetCallExpectations(test);
Case::CompositionType::setupHwcGetCallExpectations(test);
@@ -347,7 +346,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 +355,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/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 2d87ddd..c463a92 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -23,16 +23,20 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
+ ASSERT_TRUE(requestOpt); \
+ EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
+ EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
+
namespace android {
namespace {
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using DisplayModeRequest = display::DisplayModeRequest;
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
- using Action = DisplayDevice::DesiredActiveModeAction;
- using Event = scheduler::DisplayModeEvent;
-
+ using Action = DisplayDevice::DesiredModeAction;
void SetUp() override {
injectFakeBufferQueueFactory();
injectFakeNativeWindowSurfaceFactory();
@@ -43,7 +47,8 @@
PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
- mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+ mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ DisplayHotplugEvent::CONNECTED);
mFlinger.configureAndCommit();
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
@@ -64,110 +69,83 @@
ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
static inline const ftl::NonNull<DisplayModePtr> kMode120 =
ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
+
+ static inline const DisplayModeRequest kDesiredMode30{{30_Hz, kMode60}, .emitEvent = false};
+ static inline const DisplayModeRequest kDesiredMode60{{60_Hz, kMode60}, .emitEvent = true};
+ static inline const DisplayModeRequest kDesiredMode90{{90_Hz, kMode90}, .emitEvent = false};
+ static inline const DisplayModeRequest kDesiredMode120{{120_Hz, kMode120}, .emitEvent = true};
};
-TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
- EXPECT_EQ(Action::None,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None}));
- EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+TEST_F(InitiateModeChangeTest, setDesiredModeToActiveMode) {
+ EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60)));
+ EXPECT_FALSE(mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
+TEST_F(InitiateModeChangeTest, setDesiredMode) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
- ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+ mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
- // Setting another mode should be cached but return None
- EXPECT_EQ(Action::None,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
- ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+ EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
+TEST_F(InitiateModeChangeTest, clearDesiredMode) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
- ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
+ mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
+ EXPECT_TRUE(mDisplay->getDesiredMode());
- mDisplay->clearDesiredActiveModeState();
- ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+ mDisplay->clearDesiredMode();
+ EXPECT_FALSE(mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
+TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
- ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+ mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
- hal::VsyncPeriodChangeConstraints constraints{
+ const hal::VsyncPeriodChangeConstraints constraints{
.desiredTimeNanos = systemTime(),
.seamlessRequired = false,
};
hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_EQ(OK,
- mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
- &timeline));
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+ EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- mDisplay->clearDesiredActiveModeState();
- ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+ mDisplay->clearDesiredMode();
+ EXPECT_FALSE(mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, initiateRenderRateChange) {
+TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) {
EXPECT_EQ(Action::InitiateRenderRateSwitch,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None}));
- EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+ mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode30)));
+ EXPECT_FALSE(mDisplay->getDesiredMode());
}
-TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
-NO_THREAD_SAFETY_ANALYSIS {
+TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
- ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+ mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
- hal::VsyncPeriodChangeConstraints constraints{
+ const hal::VsyncPeriodChangeConstraints constraints{
.desiredTimeNanos = systemTime(),
.seamlessRequired = false,
};
hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_EQ(OK,
- mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
- &timeline));
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+ EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- EXPECT_EQ(Action::None,
- mDisplay->setDesiredActiveMode(
- {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
- ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
+ EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
- EXPECT_EQ(OK,
- mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
- &timeline));
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
- EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
+ EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode());
- mDisplay->clearDesiredActiveModeState();
- ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+ mDisplay->clearDesiredMode();
+ EXPECT_FALSE(mDisplay->getDesiredMode());
}
} // namespace
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/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index ee12276..6671414 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -481,14 +481,17 @@
constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
-template <typename PhysicalDisplay, int width, int height>
+template <typename PhysicalDisplay, int width, int height,
+ Secure secure = (PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal)
+ ? Secure::TRUE
+ : Secure::FALSE>
struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE,
- Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, secure,
+ PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
PHYSICAL_DISPLAY_FLAGS>,
HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
- Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ Async::FALSE, secure, PhysicalDisplay::PRIMARY,
GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
PhysicalDisplay> {};
@@ -515,6 +518,7 @@
};
struct TertiaryDisplay {
+ static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr uint8_t PORT = 253;
static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 8891c06..45db0c5 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -55,6 +55,9 @@
constexpr std::chrono::duration VSYNC_PERIOD(16ms);
+constexpr int HDCP_V1 = 2;
+constexpr int HDCP_V2 = 3;
+
} // namespace
class EventThreadTest : public testing::Test, public IEventThreadCallback {
@@ -471,7 +474,7 @@
mock::VSyncTracker& mockTracker =
*static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker());
- EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_, _))
.WillOnce(Return(preferredExpectedPresentationTime));
VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection);
@@ -833,6 +836,19 @@
expectVSyncCallbackScheduleReceived(true);
}
+TEST_F(EventThreadTest, postHcpLevelsChanged) {
+ setupEventThread();
+
+ mThread->onHdcpLevelsChanged(EXTERNAL_DISPLAY_ID, HDCP_V1, HDCP_V2);
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type);
+ EXPECT_EQ(EXTERNAL_DISPLAY_ID, event.header.displayId);
+ EXPECT_EQ(HDCP_V1, event.hdcpLevelsChange.connectedLevel);
+ EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 0905cd1..0c820fb 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -14,130 +14,174 @@
* limitations under the License.
*/
-#include <cstdint>
#undef LOG_TAG
#define LOG_TAG "FlagManagerTest"
-#include "FlagManager.h"
+#include <common/FlagManager.h>
+#include <common/test/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, returnsOverrideTrue) {
+ mFlagManager.markBootCompleted();
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false);
+
+ // This is stored in a static variable, so this test depends on the fact
+ // that this flag has not been read in this process.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true));
+ EXPECT_TRUE(mFlagManager.late_boot_misc2());
+
+ // Further calls will not result in further calls to getBoolProperty.
+ EXPECT_TRUE(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, returnsOverrideReadonly) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace,
+ false);
+
+ // This is stored in a static variable, so this test depends on the fact
+ // that this flag has not been read in this process.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true));
+ EXPECT_TRUE(mFlagManager.add_sf_skipped_frames_to_trace());
}
-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, returnsOverrideFalse) {
+ mFlagManager.markBootCompleted();
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ refresh_rate_overlay_on_external_display,
+ true);
+
+ // This is stored in a static variable, so this test depends on the fact
+ // that this flag has not been read in this process.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(false));
+ EXPECT_FALSE(mFlagManager.refresh_rate_overlay_on_external_display());
}
-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, ignoresOverrideInUnitTestMode) {
+ mFlagManager.setUnitTestMode();
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::multithreaded_present, true);
+
+ // If this has not been called in this process, it will be called.
+ // Regardless, the result is ignored.
+ EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(false));
+
+ EXPECT_EQ(true, mFlagManager.multithreaded_present());
}
-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, 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_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, 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_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);
+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);
}
-} // namespace
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
index a581c7a..7c1d4b4 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateOverrideMappingsTest.cpp
@@ -17,6 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "FrameRateOverrideMappingsTest"
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/test/FlagUtils.h>
#include <gtest/gtest.h>
#include <unordered_map>
@@ -34,6 +36,8 @@
};
namespace {
+using namespace com::android::graphics::surfaceflinger;
+
TEST_F(FrameRateOverrideMappingsTest, testUpdateFrameRateOverridesByContent) {
mFrameRateOverrideByContent.clear();
mFrameRateOverrideByContent.emplace(0, 30.0_Hz);
@@ -59,6 +63,8 @@
}
TEST_F(FrameRateOverrideMappingsTest, testSetGameModeRefreshRateForUid) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, false);
+
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({1, 30.0f});
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({2, 90.0f});
@@ -95,6 +101,7 @@
}
TEST_F(FrameRateOverrideMappingsTest, testGetFrameRateOverrideForUidMixed) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, false);
mFrameRateOverrideByContent.clear();
mFrameRateOverrideByContent.emplace(0, 30.0_Hz);
mFrameRateOverrideByContent.emplace(1, 60.0_Hz);
@@ -111,7 +118,6 @@
ASSERT_EQ(allFrameRateOverrides,
mFrameRateOverrideMappings.getAllFrameRateOverrides(
/*supportsFrameRateOverrideByContent*/ true));
-
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({1, 30.0f});
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({2, 90.0f});
mFrameRateOverrideMappings.setGameModeRefreshRateForUid({4, 120.0f});
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
index 20ea0c0..5c742d7 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
@@ -49,6 +49,7 @@
const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default);
const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default);
const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_DEFAULT = FrameRate(Fps(), FrameRateCompatibility::Default);
const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
FrameRateSelectionStrategyTest();
@@ -102,7 +103,7 @@
layer->getDrawingState().frameRateSelectionStrategy);
}
-TEST_P(FrameRateSelectionStrategyTest, SetChildAndGetParent) {
+TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) {
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -116,17 +117,17 @@
child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
commitTransaction();
EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
parent->getDrawingState().frameRateSelectionStrategy);
EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
child1->getDrawingState().frameRateSelectionStrategy);
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
child2->getDrawingState().frameRateSelectionStrategy);
}
-TEST_P(FrameRateSelectionStrategyTest, SetParentAndGet) {
+TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) {
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -137,7 +138,6 @@
addChild(layer2, layer3);
layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
@@ -151,20 +151,72 @@
EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
layer2->getDrawingState().frameRateSelectionStrategy);
EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
layer3->getDrawingState().frameRateSelectionStrategy);
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self);
+ layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Propagate);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
layer1->getDrawingState().frameRateSelectionStrategy);
EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
layer2->getDrawingState().frameRateSelectionStrategy);
EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
+ layer3->getDrawingState().frameRateSelectionStrategy);
+}
+
+TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) {
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+ const auto& layerFactory = GetParam();
+ auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ addChild(layer1, layer2);
+ addChild(layer2, layer3);
+
+ layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
+ layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
+ layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self);
+ commitTransaction();
+
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
+ layer1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ layer2->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_DEFAULT, layer3->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
+ layer3->getDrawingState().frameRateSelectionStrategy);
+
+ layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
+ commitTransaction();
+
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ layer2->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
+ layer3->getDrawingState().frameRateSelectionStrategy);
+
+ layer1->setFrameRate(FRAME_RATE_DEFAULT.vote);
+ commitTransaction();
+
+ EXPECT_EQ(FRAME_RATE_TREE, layer1->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
+ layer1->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Self,
+ layer2->getDrawingState().frameRateSelectionStrategy);
+ EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
+ EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
layer3->getDrawingState().frameRateSelectionStrategy);
}
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 636d852..ddc3967 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-
+#include <common/test/FlagUtils.h>
+#include "com_android_graphics_surfaceflinger_flags.h"
#include "gmock/gmock-spec-builders.h"
#include "mock/MockTimeStats.h"
#undef LOG_TAG
@@ -40,6 +41,7 @@
using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd;
using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType;
using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType;
+using ProtoJankSeverityType = perfetto::protos::FrameTimelineEvent_JankSeverityType;
using ProtoPredictionType = perfetto::protos::FrameTimelineEvent_PredictionType;
namespace android::frametimeline {
@@ -334,7 +336,9 @@
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
+ EXPECT_NE(surfaceFrame1->getJankSeverityType(), std::nullopt);
EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt);
+ EXPECT_NE(surfaceFrame2->getJankSeverityType(), std::nullopt);
}
TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
@@ -492,8 +496,10 @@
auto displayFrame0 = getDisplayFrame(0);
EXPECT_EQ(displayFrame0->getActuals().presentTime, 59);
EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown | JankType::DisplayHAL);
+ EXPECT_EQ(displayFrame0->getJankSeverityType(), JankSeverityType::Unknown);
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Unknown);
}
// Tests related to TimeStats
@@ -603,6 +609,7 @@
presentFence1->signalForTest(90);
mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -632,6 +639,7 @@
mFrameTimeline->setSfPresent(86, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) {
@@ -661,6 +669,7 @@
mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) {
@@ -690,6 +699,7 @@
mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) {
@@ -720,6 +730,7 @@
mFrameTimeline->setSfPresent(86, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
@@ -751,6 +762,7 @@
mFrameTimeline->setSfPresent(86, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) {
@@ -787,12 +799,14 @@
auto displayFrame = getDisplayFrame(0);
EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Unknown);
EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::UnknownStart);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
+ EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full);
}
/*
@@ -919,7 +933,8 @@
ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart(
int64_t cookie, int64_t token, pid_t pid, ProtoPresentType presentType, bool onTimeFinish,
- bool gpuComposition, ProtoJankType jankType, ProtoPredictionType predictionType) {
+ bool gpuComposition, ProtoJankType jankType, ProtoJankSeverityType jankSeverityType,
+ ProtoPredictionType predictionType) {
ProtoActualDisplayFrameStart proto;
proto.set_cookie(cookie);
proto.set_token(token);
@@ -928,6 +943,7 @@
proto.set_on_time_finish(onTimeFinish);
proto.set_gpu_composition(gpuComposition);
proto.set_jank_type(jankType);
+ proto.set_jank_severity_type(jankSeverityType);
proto.set_prediction_type(predictionType);
return proto;
}
@@ -948,7 +964,8 @@
ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart(
int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName,
ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition,
- ProtoJankType jankType, ProtoPredictionType predictionType, bool isBuffer) {
+ ProtoJankType jankType, ProtoJankSeverityType jankSeverityType,
+ ProtoPredictionType predictionType, bool isBuffer) {
ProtoActualSurfaceFrameStart proto;
proto.set_cookie(cookie);
proto.set_token(token);
@@ -959,6 +976,7 @@
proto.set_on_time_finish(onTimeFinish);
proto.set_gpu_composition(gpuComposition);
proto.set_jank_type(jankType);
+ proto.set_jank_severity_type(jankSeverityType);
proto.set_prediction_type(predictionType);
proto.set_is_buffer(isBuffer);
return proto;
@@ -1001,6 +1019,8 @@
EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
ASSERT_TRUE(received.has_jank_type());
EXPECT_EQ(received.jank_type(), source.jank_type());
+ ASSERT_TRUE(received.has_jank_severity_type());
+ EXPECT_EQ(received.jank_severity_type(), source.jank_severity_type());
ASSERT_TRUE(received.has_prediction_type());
EXPECT_EQ(received.prediction_type(), source.prediction_type());
}
@@ -1048,6 +1068,8 @@
EXPECT_EQ(received.gpu_composition(), source.gpu_composition());
ASSERT_TRUE(received.has_jank_type());
EXPECT_EQ(received.jank_type(), source.jank_type());
+ ASSERT_TRUE(received.has_jank_severity_type());
+ EXPECT_EQ(received.jank_severity_type(), source.jank_severity_type());
ASSERT_TRUE(received.has_prediction_type());
EXPECT_EQ(received.prediction_type(), source.prediction_type());
ASSERT_TRUE(received.has_is_buffer());
@@ -1059,6 +1081,98 @@
EXPECT_EQ(received.cookie(), source.cookie());
}
+TEST_F(FrameTimelineTest, traceDisplayFrameSkipped) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace,
+ true);
+
+ // 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::SEVERITY_NONE,
+ 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);
@@ -1084,6 +1198,7 @@
kSurfaceFlingerPid,
FrameTimelineEvent::PRESENT_ON_TIME, true, false,
FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::SEVERITY_NONE,
FrameTimelineEvent::PREDICTION_VALID);
auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 2);
@@ -1163,6 +1278,7 @@
kSurfaceFlingerPid,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
false, FrameTimelineEvent::JANK_UNKNOWN,
+ FrameTimelineEvent::SEVERITY_UNKNOWN,
FrameTimelineEvent::PREDICTION_EXPIRED);
auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1);
@@ -1238,6 +1354,7 @@
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, true, false,
FrameTimelineEvent::JANK_DROPPED,
+ FrameTimelineEvent::SEVERITY_UNKNOWN,
FrameTimelineEvent::PREDICTION_VALID, true);
auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2);
@@ -1250,6 +1367,7 @@
displayFrameToken1, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_ON_TIME, true, false,
FrameTimelineEvent::JANK_NONE,
+ FrameTimelineEvent::SEVERITY_NONE,
FrameTimelineEvent::PREDICTION_VALID, true);
auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4);
@@ -1396,6 +1514,7 @@
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
false, FrameTimelineEvent::JANK_APP_DEADLINE_MISSED,
+ FrameTimelineEvent::SEVERITY_UNKNOWN,
FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
@@ -1473,6 +1592,7 @@
displayFrameToken, sPidOne, sLayerNameOne,
FrameTimelineEvent::PRESENT_DROPPED, false, false,
FrameTimelineEvent::JANK_DROPPED,
+ FrameTimelineEvent::SEVERITY_UNKNOWN,
FrameTimelineEvent::PREDICTION_EXPIRED, true);
auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1);
@@ -1551,6 +1671,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::None);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
@@ -1577,6 +1698,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial);
// Fences for the second frame haven't been flushed yet, so it should be 0
auto displayFrame2 = getDisplayFrame(1);
@@ -1590,6 +1712,7 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
@@ -1616,6 +1739,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial);
// Fences for the second frame haven't been flushed yet, so it should be 0
auto displayFrame2 = getDisplayFrame(1);
@@ -1630,6 +1754,7 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
@@ -1652,6 +1777,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
@@ -1697,6 +1823,7 @@
EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+ EXPECT_EQ(displayFrame0->getJankSeverityType(), JankSeverityType::Full);
// case 3 - cpu time = 86 - 82 = 4, vsync period = 30
mFrameTimeline->setSfWakeUp(sfToken3, 106, RR_30, RR_30);
@@ -1711,6 +1838,7 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::Full);
// case 4 - cpu time = 86 - 82 = 4, vsync period = 30
mFrameTimeline->setSfWakeUp(sfToken4, 120, RR_30, RR_30);
@@ -1725,6 +1853,7 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Full);
addEmptyDisplayFrame();
@@ -1733,6 +1862,7 @@
EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+ EXPECT_EQ(displayFrame3->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
@@ -1785,12 +1915,14 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::Partial);
actuals1 = presentedSurfaceFrame1.getActuals();
EXPECT_EQ(actuals1.presentTime, 30);
EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial);
// Fences for the second frame haven't been flushed yet, so it should be 0
presentFence2->signalForTest(65);
@@ -1813,12 +1945,14 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Partial);
actuals2 = presentedSurfaceFrame2.getActuals();
EXPECT_EQ(actuals2.presentTime, 65);
EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
@@ -1871,12 +2005,14 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::Partial);
actuals1 = presentedSurfaceFrame1.getActuals();
EXPECT_EQ(actuals1.presentTime, 50);
EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::DisplayHAL);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial);
// Fences for the second frame haven't been flushed yet, so it should be 0
presentFence2->signalForTest(86);
@@ -1899,12 +2035,14 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Full);
actuals2 = presentedSurfaceFrame2.getActuals();
EXPECT_EQ(actuals2.presentTime, 86);
EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
@@ -1941,12 +2079,14 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::None);
actuals1 = presentedSurfaceFrame1.getActuals();
EXPECT_EQ(actuals1.presentTime, 50);
EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::Unknown);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
@@ -2003,12 +2143,14 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::None);
actuals1 = presentedSurfaceFrame1.getActuals();
EXPECT_EQ(actuals1.presentTime, 40);
EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial);
// Fences for the second frame haven't been flushed yet, so it should be 0
presentFence2->signalForTest(60);
@@ -2023,6 +2165,7 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Partial);
actuals2 = presentedSurfaceFrame2.getActuals();
EXPECT_EQ(actuals2.presentTime, 60);
@@ -2030,6 +2173,7 @@
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Partial);
}
TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
@@ -2089,10 +2233,12 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::None);
EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Full);
// Fences for the second frame haven't been flushed yet, so it should be 0
EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
@@ -2109,11 +2255,13 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::None);
EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
JankType::AppDeadlineMissed | JankType::BufferStuffing);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffing) {
@@ -2174,10 +2322,12 @@
EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::None);
EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Full);
// Fences for the second frame haven't been flushed yet, so it should be 0
EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
@@ -2194,10 +2344,12 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::None);
EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::BufferStuffing);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent_GpuAndCpuMiss) {
@@ -2225,6 +2377,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Full);
// Case 2: No GPU fence so it will not use GPU composition.
mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_30, RR_30);
@@ -2242,6 +2395,7 @@
EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+ EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Full);
}
TEST_F(FrameTimelineTest, jankClassification_presentFenceError) {
@@ -2272,6 +2426,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Unknown);
}
{
auto displayFrame = getDisplayFrame(1);
@@ -2279,6 +2434,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Unknown);
}
{
auto displayFrame = getDisplayFrame(2);
@@ -2286,6 +2442,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+ EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::None);
}
}
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 4f545a9..a5c0657 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -21,6 +21,7 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <optional>
#include <vector>
// StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be
@@ -30,10 +31,12 @@
#include <gmock/gmock.h>
#pragma clang diagnostic pop
+#include <common/FlagManager.h>
#include <gui/LayerMetadata.h>
#include <log/log.h>
#include <chrono>
+#include <common/test/FlagUtils.h>
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
@@ -41,6 +44,8 @@
#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"
@@ -53,11 +58,11 @@
using Hwc2::Config;
+using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using hal::IComposerClient;
using ::testing::_;
using ::testing::DoAll;
-using ::testing::ElementsAreArray;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
@@ -79,11 +84,7 @@
EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
- void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) {
- ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end());
- auto& displayData = mHwc.mDisplayData.at(displayId);
- displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
- }
+ void setVrrTimeoutHint(bool status) { mHwc.mEnableVrrTimeout = status; }
};
TEST_F(HWComposerTest, isHeadless) {
@@ -136,7 +137,7 @@
const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
ASSERT_TRUE(info);
- EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
{
EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
@@ -214,18 +215,24 @@
}
}
-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(true));
+ EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
{
- EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
.WillOnce(Return(HalError::BAD_DISPLAY));
EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
}
@@ -234,13 +241,101 @@
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
+ expectHotplugConnect(kHwcDisplayId);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+
+ EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(true));
+
+ {
+ EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
+ .WillOnce(Return(HalError::BAD_DISPLAY));
+ EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+ }
+ {
+ setVrrTimeoutHint(true);
+ constexpr int32_t kWidth = 480;
+ constexpr int32_t kHeight = 720;
+ constexpr int32_t kConfigGroup = 1;
+ constexpr int32_t kVsyncPeriod = 16666667;
const hal::VrrConfig vrrConfig =
hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
.notifyExpectedPresentConfig = hal::VrrConfig::
- NotifyExpectedPresentConfig{.notifyExpectedPresentHeadsUpNs =
- ms2ns(30),
- .notifyExpectedPresentTimeoutNs =
- ms2ns(30)}};
+ NotifyExpectedPresentConfig{.headsUpNs = ms2ns(30),
+ .timeoutNs = ms2ns(30)}};
hal::DisplayConfiguration displayConfiguration{.configId = kConfigId,
.width = kWidth,
.height = kHeight,
@@ -270,9 +365,9 @@
displayConfiguration.dpi = {kDpi, kDpi};
EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
- .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
- displayConfiguration}),
- Return(HalError::NONE)));
+ .WillRepeatedly(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
+ displayConfiguration}),
+ Return(HalError::NONE)));
modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
EXPECT_EQ(modes.size(), size_t{1});
@@ -284,6 +379,10 @@
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
EXPECT_EQ(modes.front().dpiX, kDpi);
EXPECT_EQ(modes.front().dpiY, kDpi);
+
+ setVrrTimeoutHint(false);
+ modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+ EXPECT_EQ(modes.front().vrrConfig->notifyExpectedPresentConfig, std::nullopt);
}
}
@@ -315,57 +414,9 @@
EXPECT_FALSE(displayIdOpt);
}
-TEST_F(HWComposerTest, notifyExpectedPresentTimeout) {
- constexpr hal::HWDisplayId kHwcDisplayId = 2;
- expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
- 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);
-
- ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0));
-
- {
- // Very first ExpectedPresent after idle, no previous timestamp
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
- kTimeoutNs);
- }
- {
- // ExpectedPresent is after the timeoutNs
- expectedPresentTime += ms2ns(50);
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
- kTimeoutNs);
- }
- {
- // ExpectedPresent is after the last reported ExpectedPresent.
- expectedPresentTime += ms2ns(10);
- EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
- 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);
- EXPECT_CALL(*mHal,
- notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs))
- .WillOnce(Return(HalError::NONE));
- mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs,
- kTimeoutNs);
- }
-}
-
struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
- MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
+ MOCK_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent),
+ (override));
MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
MOCK_METHOD3(onComposerHalVsync,
void(hal::HWDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos>));
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 95f1940..2b333f4 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -45,7 +45,8 @@
// reparenting tests
TEST_F(LayerHierarchyTest, addLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -64,7 +65,8 @@
}
TEST_F(LayerHierarchyTest, reparentLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(2, 11);
reparentLayer(111, 12);
reparentLayer(1221, 1);
@@ -79,7 +81,8 @@
}
TEST_F(LayerHierarchyTest, reparentLayerToNull) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(2, UNASSIGNED_LAYER_ID);
reparentLayer(11, UNASSIGNED_LAYER_ID);
@@ -96,7 +99,8 @@
}
TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(2, UNASSIGNED_LAYER_ID);
reparentLayer(11, UNASSIGNED_LAYER_ID);
reparentLayer(1221, UNASSIGNED_LAYER_ID);
@@ -115,7 +119,8 @@
}
TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
destroyLayerHandle(111);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -139,7 +144,8 @@
}
TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
reparentLayer(11, 1);
@@ -154,7 +160,8 @@
// offscreen tests
TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -170,7 +177,8 @@
}
TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -187,7 +195,8 @@
// rel-z tests
TEST_F(LayerHierarchyTest, setRelativeParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -200,7 +209,8 @@
}
TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -216,7 +226,8 @@
}
TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -231,7 +242,8 @@
}
TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -246,7 +258,8 @@
}
TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -262,7 +275,8 @@
}
TEST_F(LayerHierarchyTest, reparentRelativeLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -294,7 +308,8 @@
// mirror tests
TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -308,7 +323,8 @@
}
TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(11, UNASSIGNED_LAYER_ID);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
@@ -324,7 +340,8 @@
TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
createLayer(1111, 111);
createLayer(112, 11);
@@ -340,7 +357,8 @@
// mirror & relatives tests
TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(111, 12);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
@@ -371,7 +389,8 @@
}
TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(1221, 12);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
@@ -401,7 +420,8 @@
}
TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(11, 2);
reparentLayer(2, UNASSIGNED_LAYER_ID);
@@ -427,7 +447,8 @@
}
TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentRelativeLayer(1221, 2);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -462,7 +483,8 @@
}
TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
updateBackgroundColor(1, 0.5);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -485,7 +507,8 @@
createLayer(11, 1);
reparentLayer(1, 11);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
std::vector<uint32_t> expectedTraversalPath = {};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -502,17 +525,11 @@
createLayer(11, 1);
reparentRelativeLayer(11, 2);
reparentRelativeLayer(2, 11);
- mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
-
- // fix loop
- uint32_t invalidRelativeRoot;
- bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
- EXPECT_TRUE(hasRelZLoop);
- mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
- hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
- EXPECT_EQ(invalidRelativeRoot, 11u);
- EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+ LayerHierarchyBuilder hierarchyBuilder;
+ // this call is expected to fix the loop!
+ hierarchyBuilder.update(mLifecycleManager);
+ uint32_t unused;
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused));
std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -534,16 +551,11 @@
createLayer(221, 22);
reparentRelativeLayer(22, 111);
reparentRelativeLayer(11, 221);
- mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
-
- // fix loop
- uint32_t invalidRelativeRoot;
- bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
- EXPECT_TRUE(hasRelZLoop);
- mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
- hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
- EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+ LayerHierarchyBuilder hierarchyBuilder;
+ // this call is expected to fix the loop!
+ hierarchyBuilder.update(mLifecycleManager);
+ uint32_t unused;
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(unused));
std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -554,7 +566,8 @@
}
TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(1, UNASSIGNED_LAYER_ID);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -568,7 +581,8 @@
TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
// remove default hierarchy
mLifecycleManager = LayerLifecycleManager();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
createRootLayer(1);
destroyLayerHandle(1);
UPDATE_AND_VERIFY(hierarchyBuilder);
@@ -582,7 +596,8 @@
// traversal path test
TEST_F(LayerHierarchyTest, traversalPathId) {
setZ(122, -1);
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
auto checkTraversalPathIdVisitor =
[](const LayerHierarchy& hierarchy,
const LayerHierarchy::TraversalPath& traversalPath) -> bool {
@@ -605,7 +620,8 @@
createLayer(53, 5);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -639,7 +655,8 @@
setZ(13, 1);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -661,7 +678,8 @@
setLayerStack(2, 10);
mLifecycleManager.commitChanges();
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
UPDATE_AND_VERIFY(hierarchyBuilder);
std::vector<uint32_t> expectedTraversalPath = {2, 21, 1, 11};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
@@ -672,7 +690,8 @@
}
TEST_F(LayerHierarchyTest, canMirrorDisplay) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -687,7 +706,8 @@
}
TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5));
setLayerStack(3, 1);
@@ -701,7 +721,8 @@
}
TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -719,7 +740,8 @@
}
TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -737,7 +759,8 @@
}
TEST_F(LayerHierarchyTest, canMirrorDisplayWithMirrors) {
- LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
reparentLayer(12, UNASSIGNED_LAYER_ID);
mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
UPDATE_AND_VERIFY(hierarchyBuilder);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 48f8923..67e6249 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -176,14 +176,12 @@
void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
- if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
- hierarchyBuilder.update(mLifecycleManager.getLayers(),
- mLifecycleManager.getDestroyedLayers());
- }
+ hierarchyBuilder.update(mLifecycleManager);
mLifecycleManager.commitChanges();
// rebuild layer hierarchy from scratch and verify that it matches the updated state.
- LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+ LayerHierarchyBuilder newBuilder;
+ newBuilder.update(mLifecycleManager);
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
getTraversalPath(newBuilder.getHierarchy()));
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
@@ -478,6 +476,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eDropInputModeChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.dropInputMode = dropInputMode;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
@@ -501,10 +510,7 @@
}
void update(LayerSnapshotBuilder& snapshotBuilder) {
- if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
- mHierarchyBuilder.update(mLifecycleManager.getLayers(),
- mLifecycleManager.getDestroyedLayers());
- }
+ mHierarchyBuilder.update(mLifecycleManager);
LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLifecycleManager,
.includeMetadata = false,
@@ -519,7 +525,7 @@
mLifecycleManager.commitChanges();
}
- LayerHierarchyBuilder mHierarchyBuilder{{}};
+ LayerHierarchyBuilder mHierarchyBuilder;
DisplayInfos mFrontEndDisplayInfos;
bool mHasDisplayChanges = false;
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 631adf1..734fddb 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 <common/test/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:
@@ -53,7 +56,7 @@
LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
mFlinger.resetScheduler(mScheduler);
mLifecycleManager = {};
- mHierarchyBuilder = {{}};
+ mHierarchyBuilder = {};
}
void updateLayerSnapshotsAndLayerHistory(nsecs_t now) {
@@ -162,10 +165,12 @@
DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
-
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
TestableSurfaceFlinger mFlinger;
+
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback);
};
namespace {
@@ -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);
@@ -736,6 +768,7 @@
}
TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) {
+ SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -747,6 +780,20 @@
recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
}
+TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
+ SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) {
auto layer = createLegacyAndFrontedEndLayer(1);
@@ -807,6 +854,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..9456e37 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 <common/test/FlagUtils.h>
#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
@@ -116,6 +118,9 @@
auto createLayer(std::string name) {
return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
}
+ auto createLayer(std::string name, uint32_t uid) {
+ return sp<MockLayer>::make(mFlinger.flinger(), std::move(name), std::move(uid));
+ }
void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
Fps desiredRefreshRate, int numFrames) {
@@ -142,13 +147,16 @@
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
-
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
TestableSurfaceFlinger mFlinger;
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback);
};
namespace {
+using namespace com::android::graphics::surfaceflinger;
+
TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -241,6 +249,105 @@
}
}
+TEST_F(LayerHistoryTest, gameFrameRateOverrideMapping) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
+
+ auto overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(120_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+}
+
+TEST_F(LayerHistoryTest, oneLayerGameFrameRateOverride) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ const uid_t uid = 0;
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const Fps gameModeFrameRate = Fps::fromValue(60.0f);
+ const auto layer = createLayer("GameFrameRateLayer", uid);
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ // update game default frame rate override
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ nsecs_t time = systemTime();
+ LayerHistory::Summary summary;
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += gameDefaultFrameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
+
+ // test against setFrameRate vote
+ const Fps setFrameRate = Fps::fromValue(120.0f);
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default)));
+
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += setFrameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
+
+ // update game mode frame rate override
+ history().updateGameModeFrameRateOverride(
+ FrameRateOverride({uid, gameModeFrameRate.getValue()}));
+
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += gameModeFrameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
+}
+
TEST_F(LayerHistoryTest, oneInvisibleLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -555,6 +662,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 +914,8 @@
}
TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
+ SET_FLAG_FOR_TEST(flags::misc1, false);
+
auto explicitVisiblelayer = createLayer();
auto explicitInvisiblelayer = createLayer();
@@ -810,6 +946,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 +1028,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();
@@ -1064,6 +1270,8 @@
}
TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
+ SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
+
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
@@ -1077,6 +1285,23 @@
recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
}
+TEST_F(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
+ SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
+
+ const auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
TEST_F(LayerHistoryTest, smallDirtyLayer) {
auto layer = createLayer();
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 11072bc..22cfbd8 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -21,6 +21,7 @@
#include <scheduler/Fps.h>
+#include <common/test/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;
TestableSurfaceFlinger mFlinger;
+ TestableScheduler* mScheduler =
+ new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback);
};
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..55b20b3 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -58,8 +58,7 @@
void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
- mHierarchyBuilder.update(mLifecycleManager.getLayers(),
- mLifecycleManager.getDestroyedLayers());
+ mHierarchyBuilder.update(mLifecycleManager);
}
args.root = mHierarchyBuilder.getHierarchy();
actualBuilder.update(args);
@@ -651,7 +650,7 @@
// │ └── 13
// └── 2
setFrameRate(11, 244.f, 0, 0);
- setFrameRateCategory(122, 3 /* Normal */);
+ setFrameRateCategory(122, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
// verify parent 1 gets no vote
@@ -789,6 +788,231 @@
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::Propagate);
+ 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::Propagate);
+ 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::Propagate);
+ 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::Propagate);
+ 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, ANATIVEWINDOW_FRAME_RATE_CATEGORY_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, ANATIVEWINDOW_FRAME_RATE_CATEGORY_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::Propagate);
+ 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::Propagate);
+ 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::Propagate);
+ 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::Propagate);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Default);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithOverrideChildrenAndSelf) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11 (frame rate set to 11.f with strategy Self)
+ // │ │ └── 111 (frame rate is not inherited)
+ // │ ├── 12 (frame rate set to 244.f)
+ // │ │ ├── 121
+ // │ │ └── 122 (strategy OverrideChildren and inherits frame rate 244.f)
+ // │ │ └── 1221 (frame rate set to 123.f but should be overridden by layer 122)
+ // │ └── 13
+ // └── 2
+ setFrameRate(11, 11.f, 0, 0);
+ setFrameRateSelectionStrategy(11, 2 /* Self */);
+ setFrameRate(12, 244.f, 0, 0);
+ setFrameRateSelectionStrategy(122, 1 /* OverrideChildren */);
+ setFrameRate(1221, 123.f, 0, 0);
+
+ 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})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 11.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+ EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 11 does does not propagate its framerate to 111.
+ EXPECT_FALSE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate);
+ EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate);
+ EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
+ 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.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // ROOT
+ // ├── 1 (frame rate set to 1.f with strategy OverrideChildren)
+ // │ ├── 11 (frame rate set to 11.f with strategy Self, but overridden by 1)
+ // │ │ └── 111 (frame rate inherited from 11 due to override from 1)
+ // â‹® â‹®
+ setFrameRate(1, 1.f, 0, 0);
+ setFrameRateSelectionStrategy(1, 1 /* OverrideChildren */);
+ setFrameRate(11, 11.f, 0, 0);
+ setFrameRateSelectionStrategy(11, 2 /* Self */);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.rate.getValue(), 1.f);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 1.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 11 does does not propagate its framerate to 111.
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 1.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRateSelectionStrategy,
+ scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+ EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
}
TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
@@ -898,4 +1122,64 @@
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());
+}
+
+TEST_F(LayerSnapshotTest, setSecureRootSnapshot) {
+ setFlags(1, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = false,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ args.rootSnapshot.isSecure = true;
+ update(mSnapshotBuilder, args);
+
+ EXPECT_TRUE(getSnapshot(1)->isSecure);
+ // Ensure child is also marked as secure
+ EXPECT_TRUE(getSnapshot(11)->isSecure);
+}
+
+// b/314350323
+TEST_F(LayerSnapshotTest, propagateDropInputMode) {
+ setDropInputMode(1, gui::DropInputMode::ALL);
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = false,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ args.rootSnapshot.isSecure = true;
+ update(mSnapshotBuilder, args);
+
+ EXPECT_EQ(getSnapshot(1)->dropInputMode, gui::DropInputMode::ALL);
+ // Ensure child also has the correct drop input mode regardless of whether either layer has
+ // an input channel
+ EXPECT_EQ(getSnapshot(11)->dropInputMode, gui::DropInputMode::ALL);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 9aa089f..e9c4d80 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -91,7 +91,7 @@
TEST_F(MessageQueueTest, commit) {
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
@@ -105,7 +105,7 @@
InSequence s;
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
@@ -124,7 +124,7 @@
InSequence s;
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
@@ -151,7 +151,7 @@
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
.readyDuration = 0,
- .earliestVsync = kPresentTime.ns()};
+ .lastVsync = kPresentTime.ns()};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
@@ -163,7 +163,7 @@
const auto timing =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDifferentDuration.ns(),
.readyDuration = 0,
- .earliestVsync = 0};
+ .lastVsync = 0};
EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 85f66f4..9c66a97 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -24,6 +24,7 @@
#include <powermanager/PowerHalWrapper.h>
#include <ui/DisplayId.h>
#include <chrono>
+#include <future>
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockIPowerHintSession.h"
#include "mock/DisplayHardware/MockPowerHalController.h"
@@ -40,11 +41,14 @@
class PowerAdvisorTest : public testing::Test {
public:
void SetUp() override;
- void startPowerHintSession();
+ void startPowerHintSession(bool returnValidSession = true);
void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
Duration getFenceWaitDelayDuration(bool skipValidate);
Duration getErrorMargin();
+ void setTimingTestingMode(bool testinMode);
+ void allowReportActualToAcquireMutex();
+ bool sessionExists();
protected:
TestableSurfaceFlinger mFlinger;
@@ -53,6 +57,11 @@
std::shared_ptr<MockIPowerHintSession> mMockPowerHintSession;
};
+bool PowerAdvisorTest::sessionExists() {
+ std::scoped_lock lock(mPowerAdvisor->mHintSessionMutex);
+ return mPowerAdvisor->mHintSession != nullptr;
+}
+
void PowerAdvisorTest::SetUp() {
mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
@@ -62,14 +71,20 @@
.WillByDefault(Return(HalResult<int64_t>::fromStatus(binder::Status::ok(), 16000)));
}
-void PowerAdvisorTest::startPowerHintSession() {
- const std::vector<int32_t> threadIds = {1, 2, 3};
+void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
mMockPowerHintSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+ if (returnValidSession) {
+ ON_CALL(*mMockPowerHalController, createHintSession)
+ .WillByDefault(
+ Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+ } else {
+ ON_CALL(*mMockPowerHalController, createHintSession)
+ .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(binder::Status::ok(), nullptr)));
+ }
mPowerAdvisor->enablePowerHintSession(true);
- mPowerAdvisor->startPowerHintSession(threadIds);
+ mPowerAdvisor->startPowerHintSession({1, 2, 3});
ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
.WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
}
@@ -86,6 +101,14 @@
mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod);
}
+void PowerAdvisorTest::setTimingTestingMode(bool testingMode) {
+ mPowerAdvisor->mTimingTestingMode = testingMode;
+}
+
+void PowerAdvisorTest::allowReportActualToAcquireMutex() {
+ mPowerAdvisor->mDelayReportActualMutexAcquisitonPromise.set_value(true);
+}
+
Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
: PowerAdvisor::kFenceWaitStartDelayValidated);
@@ -221,5 +244,131 @@
mPowerAdvisor->reportActualWorkDuration();
}
+TEST_F(PowerAdvisorTest, hintSessionValidWhenNullFromPowerHAL) {
+ mPowerAdvisor->onBootFinished();
+
+ startPowerHintSession(false);
+
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+
+ // 60hz
+ const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+ const Duration presentDuration = 5ms;
+ const Duration postCompDuration = 1ms;
+
+ TimePoint startTime{100ns};
+
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+
+ // increment the frame
+ startTime += vsyncPeriod;
+
+ const Duration expectedDuration = getErrorMargin() + presentDuration + postCompDuration;
+ EXPECT_CALL(*mMockPowerHintSession,
+ reportActualWorkDuration(ElementsAre(
+ Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
+ .Times(0);
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->reportActualWorkDuration();
+}
+
+TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
+ EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).Times(1);
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+ mPowerAdvisor->startPowerHintSession({1, 2, 3});
+}
+
+TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) {
+ // notifyDisplayUpdateImminentAndCpuReset or notifyCpuLoadUp gets called in background
+ // reportActual gets called during callback and sees true session, passes ensure
+ // first notify finishes, setting value to true. Another async method gets called, acquires the
+ // lock between reportactual finishing ensure and acquiring the lock itself, and sets session to
+ // nullptr. reportActual acquires the lock, and the session is now null, so it does nullptr
+ // deref
+
+ mPowerAdvisor->onBootFinished();
+ startPowerHintSession();
+
+ // --- fake a bunch of timing data
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
+ // 60hz
+ const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+ const Duration presentDuration = 5ms;
+ const Duration postCompDuration = 1ms;
+ TimePoint startTime{100ns};
+ // advisor only starts on frame 2 so do an initial no-op frame
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);
+ // increment the frame
+ startTime += vsyncPeriod;
+ fakeBasicFrameTiming(startTime, vsyncPeriod);
+ setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
+ mPowerAdvisor->setDisplays(displayIds);
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
+ // --- Done faking timing data
+
+ setTimingTestingMode(true);
+ std::promise<bool> letSendHintFinish;
+
+ ON_CALL(*mMockPowerHintSession, sendHint).WillByDefault([&letSendHintFinish] {
+ letSendHintFinish.get_future().wait();
+ return ndk::ScopedAStatus::fromExceptionCode(-127);
+ });
+
+ ON_CALL(*mMockPowerHintSession, reportActualWorkDuration).WillByDefault([] {
+ return ndk::ScopedAStatus::fromExceptionCode(-127);
+ });
+
+ ON_CALL(*mMockPowerHalController, createHintSession)
+ .WillByDefault(Return(
+ HalResult<std::shared_ptr<IPowerHintSession>>::
+ fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr)));
+
+ // First background call, to notice the session is down
+ auto firstHint = std::async(std::launch::async, [this] {
+ mPowerAdvisor->notifyCpuLoadUp();
+ return true;
+ });
+ std::this_thread::sleep_for(10ms);
+
+ // Call reportActual while callback is resolving to try and sneak past ensure
+ auto reportActual =
+ std::async(std::launch::async, [this] { mPowerAdvisor->reportActualWorkDuration(); });
+
+ std::this_thread::sleep_for(10ms);
+ // Let the first call finish
+ letSendHintFinish.set_value(true);
+ letSendHintFinish = std::promise<bool>{};
+ firstHint.wait();
+
+ // Do the second notify call, to ensure the session is nullptr
+ auto secondHint = std::async(std::launch::async, [this] {
+ mPowerAdvisor->notifyCpuLoadUp();
+ return true;
+ });
+ letSendHintFinish.set_value(true);
+ secondHint.wait();
+ // Let report finish, potentially dereferencing
+ allowReportActualToAcquireMutex();
+ reportActual.wait();
+ EXPECT_EQ(sessionExists(), false);
+}
+
} // namespace
} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index faa12a1..1e526ba 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -26,6 +26,7 @@
#include <log/log.h>
#include <ui/Size.h>
+#include <common/test/FlagUtils.h>
#include <scheduler/Fps.h>
#include <scheduler/FrameRateMode.h>
#include "DisplayHardware/HWC2.h"
@@ -1149,6 +1150,75 @@
EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitGte) {
+ auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitGte";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 25_Hz;
+ lr1.name = "25Hz ExplicitGte";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 91_Hz;
+ lr1.name = "91Hz ExplicitGte";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitGte";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitGte";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitGte";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitGte";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.name = "90Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr);
+
+ lr1.vote = LayerVoteType::ExplicitGte;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitGte";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.name = "120Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr);
+}
+
TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
@@ -1537,12 +1607,89 @@
}
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighHint) {
+ auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ auto actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId());
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ EXPECT_EQ(30_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId());
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::HighHint;
+ lr2.name = "ExplicitCategory HighHint#2";
+ actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId());
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::Low;
+ lr2.name = "ExplicitCategory Low";
+ actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ EXPECT_EQ(30_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId());
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId());
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::ExplicitExact;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExact";
+ actualFrameRateMode = selector.getBestFrameRateMode(layers);
+ if (selector.supportsAppFrameRateOverrideByContent()) {
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId());
+ } else {
+ EXPECT_EQ(30_Hz, actualFrameRateMode.fps);
+ EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId());
+ }
+}
+
TEST_P(RefreshRateSelectorTest,
getBestFrameRateMode_withFrameRateCategory_smoothSwitchOnly_60_120_nonVrr) {
if (GetParam() != Config::FrameRateOverride::Enabled) {
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, false);
// VRR compatibility is determined by the presence of a vrr config in the DisplayMode.
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120);
@@ -1568,7 +1715,7 @@
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 120_Hz, kModeId120},
{FrameRateCategory::Normal, true, 40_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
@@ -1610,6 +1757,7 @@
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
// VRR compatibility is determined by the presence of a vrr config in the DisplayMode.
auto selector = createSelector(kVrrModes_60_120, kModeId120);
@@ -1624,15 +1772,15 @@
// Note that `smoothSwitchOnly` should not have an effect.
const std::initializer_list<Case> testCases = {
- {FrameRateCategory::Default, false, 240_Hz},
+ {FrameRateCategory::Default, false, 120_Hz},
// TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
- {FrameRateCategory::NoPreference, false, 240_Hz},
+ {FrameRateCategory::NoPreference, false, 120_Hz},
{FrameRateCategory::Low, false, 30_Hz},
{FrameRateCategory::Normal, false, 60_Hz},
{FrameRateCategory::High, false, 120_Hz},
- {FrameRateCategory::Default, true, 240_Hz},
+ {FrameRateCategory::Default, true, 120_Hz},
// TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
- {FrameRateCategory::NoPreference, true, 240_Hz},
+ {FrameRateCategory::NoPreference, true, 120_Hz},
{FrameRateCategory::Low, true, 30_Hz},
{FrameRateCategory::Normal, true, 60_Hz},
{FrameRateCategory::High, true, 120_Hz},
@@ -3486,10 +3634,11 @@
// VRR tests
TEST_P(RefreshRateSelectorTest, singleMinMaxRateForVrr) {
- if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
auto selector = createSelector(kVrrMode_120, kModeId120);
EXPECT_TRUE(selector.supportsFrameRateOverride());
@@ -3505,10 +3654,11 @@
}
TEST_P(RefreshRateSelectorTest, renderRateChangesWithPolicyChangeForVrr) {
- if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
auto selector = createSelector(kVrrModes_60_120, kModeId120);
const FpsRange only120 = {120_Hz, 120_Hz};
@@ -3562,10 +3712,11 @@
}
TEST_P(RefreshRateSelectorTest, modeChangesWithPolicyChangeForVrr) {
- if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
auto selector = createSelector(kVrrModes_60_120, kModeId120);
const FpsRange range120 = {0_Hz, 120_Hz};
@@ -3585,10 +3736,11 @@
}
TEST_P(RefreshRateSelectorTest, getFrameRateOverridesForVrr) {
- if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
auto selector = createSelector(kVrrMode_120, kModeId120);
// TODO(b/297600226) Run at lower than 30 Fps for dVRR
const std::vector<Fps> desiredRefreshRates = {30_Hz, 34.285_Hz, 40_Hz, 48_Hz,
@@ -3614,10 +3766,11 @@
}
TEST_P(RefreshRateSelectorTest, renderFrameRatesForVrr) {
- if (GetParam() != Config::FrameRateOverride::Enabled || !flags::vrr_config()) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
return;
}
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
auto selector = createSelector(kVrrMode_120, kModeId120);
const FpsRange only120 = {120_Hz, 120_Hz};
const FpsRange range120 = {0_Hz, 120_Hz};
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 87fae2c..6986689 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <common/test/FlagUtils.h>
#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -23,6 +24,7 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateSelector.h"
+#include "Scheduler/VSyncPredictor.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -32,11 +34,15 @@
#include <FrontEnd/LayerHierarchy.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
#include "FpsOps.h"
+using namespace com::android::graphics::surfaceflinger;
+
namespace android::scheduler {
using android::mock::createDisplayMode;
+using android::mock::createVrrDisplayMode;
using testing::_;
using testing::Return;
@@ -89,14 +95,15 @@
kDisplay1Mode60->getId());
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
- surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
+ mock::VsyncTrackerCallback mVsyncTrackerCallback;
+ TestableSurfaceFlinger mFlinger;
+ TestableScheduler* mScheduler =
+ new TestableScheduler{mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback};
+ surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
-
- TestableSurfaceFlinger mFlinger;
};
SchedulerTest::SchedulerTest() {
@@ -455,26 +462,51 @@
using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>;
struct Compositor final : ICompositor {
- VsyncIds vsyncIds;
+ explicit Compositor(TestableScheduler& scheduler) : scheduler(scheduler) {}
+
+ TestableScheduler& scheduler;
+
+ struct {
+ PhysicalDisplayId commit;
+ PhysicalDisplayId composite;
+ } pacesetterIds;
+
+ struct {
+ VsyncIds commit;
+ VsyncIds composite;
+ } vsyncIds;
+
bool committed = true;
+ bool changePacesetter = false;
void configure() override {}
- bool commit(PhysicalDisplayId, const scheduler::FrameTargets& targets) override {
- vsyncIds.clear();
+ bool commit(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargets& targets) override {
+ pacesetterIds.commit = pacesetterId;
+
+ vsyncIds.commit.clear();
+ vsyncIds.composite.clear();
for (const auto& [id, target] : targets) {
- vsyncIds.emplace_back(id, target->vsyncId());
+ vsyncIds.commit.emplace_back(id, target->vsyncId());
+ }
+
+ if (changePacesetter) {
+ scheduler.setPacesetterDisplay(kDisplayId2);
}
return committed;
}
- CompositeResultsPerDisplay composite(PhysicalDisplayId,
- const scheduler::FrameTargeters&) override {
+ CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId,
+ const scheduler::FrameTargeters& targeters) override {
+ pacesetterIds.composite = pacesetterId;
+
CompositeResultsPerDisplay results;
- for (const auto& [id, _] : vsyncIds) {
+ for (const auto& [id, targeter] : targeters) {
+ vsyncIds.composite.emplace_back(id, targeter->target().vsyncId());
results.try_emplace(id,
CompositeResult{.compositionCoverage =
CompositionCoverage::Hwc});
@@ -484,21 +516,175 @@
}
void sample() override {}
- } compositor;
+ } compositor(*mScheduler);
mScheduler->doFrameSignal(compositor, VsyncId(42));
- const auto makeVsyncIds = [](VsyncId vsyncId) -> VsyncIds {
- return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}};
+ const auto makeVsyncIds = [](VsyncId vsyncId, bool swap = false) -> VsyncIds {
+ if (swap) {
+ return {{kDisplayId2, vsyncId}, {kDisplayId1, vsyncId}};
+ } else {
+ return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}};
+ }
};
- EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds);
+ EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.commit);
+ EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.composite);
+ EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds.commit);
+ EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds.composite);
+ // FrameTargets should be updated despite the skipped commit.
compositor.committed = false;
mScheduler->doFrameSignal(compositor, VsyncId(43));
- // FrameTargets should be updated despite the skipped commit.
- EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds);
+ EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.commit);
+ EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.composite);
+ EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds.commit);
+ EXPECT_TRUE(compositor.vsyncIds.composite.empty());
+
+ // The pacesetter may change during commit.
+ compositor.committed = true;
+ compositor.changePacesetter = true;
+ mScheduler->doFrameSignal(compositor, VsyncId(44));
+
+ EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.commit);
+ EXPECT_EQ(kDisplayId2, compositor.pacesetterIds.composite);
+ EXPECT_EQ(makeVsyncIds(VsyncId(44)), compositor.vsyncIds.commit);
+ EXPECT_EQ(makeVsyncIds(VsyncId(44), true), compositor.vsyncIds.composite);
+}
+
+TEST_F(SchedulerTest, nextFrameIntervalTest) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ static constexpr size_t kHistorySize = 10;
+ static constexpr size_t kMinimumSamplesForPrediction = 6;
+ static constexpr size_t kOutlierTolerancePercent = 25;
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ auto frameRate = Fps::fromPeriodNsecs(1000);
+
+ const ftl::NonNull<DisplayModePtr> kMode = ftl::as_non_null(
+ createVrrDisplayMode(DisplayModeId(0), refreshRate,
+ hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>(
+ frameRate.getPeriodNsecs())}));
+ std::shared_ptr<VSyncPredictor> vrrTracker =
+ std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent, mVsyncTrackerCallback);
+ std::shared_ptr<RefreshRateSelector> vrrSelectorPtr =
+ std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId());
+ TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(),
+ vrrTracker,
+ vrrSelectorPtr,
+ mFlinger.getFactory(),
+ mFlinger.getTimeStats(),
+ mSchedulerCallback,
+ mVsyncTrackerCallback};
+
+ scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker);
+ vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
+ scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
+ vrrTracker->addVsyncTimestamp(0);
+
+ EXPECT_EQ(Fps::fromPeriodNsecs(1000),
+ scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
+ TimePoint::fromNs(1000)));
+ EXPECT_EQ(Fps::fromPeriodNsecs(1000),
+ scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
+ TimePoint::fromNs(2000)));
+
+ // Not crossing the min frame period
+ EXPECT_EQ(Fps::fromPeriodNsecs(1500),
+ scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
+ TimePoint::fromNs(2500)));
+ // Change render rate
+ frameRate = Fps::fromPeriodNsecs(2000);
+ vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
+ scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
+
+ EXPECT_EQ(Fps::fromPeriodNsecs(2000),
+ scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
+ TimePoint::fromNs(2000)));
+ EXPECT_EQ(Fps::fromPeriodNsecs(2000),
+ scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
+ TimePoint::fromNs(4000)));
+}
+
+TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
+ // resyncAllToHardwareVsync will result in requesting hardware VSYNC on both displays, since
+ // they are both on.
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1);
+
+ mScheduler->registerDisplay(kDisplayId2,
+ std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+ kDisplay2Mode60->getId()));
+ mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+ mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON);
+
+ static constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+ mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
+
+ static constexpr bool kAllowToEnable = true;
+ mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
+TEST_F(SchedulerTest, resyncAllDoNotAllow) FTL_FAKE_GUARD(kMainThreadContext) {
+ // Without setting allowToEnable to true, resyncAllToHardwareVsync does not
+ // result in requesting hardware VSYNC.
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, _)).Times(0);
+
+ mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+
+ static constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+
+ static constexpr bool kAllowToEnable = false;
+ mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
+TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+
+ // resyncAllToHardwareVsync will result in requesting hardware VSYNC on display 1, which is on,
+ // but not on display 2, which is off.
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, _)).Times(0);
+
+ mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+
+ mScheduler->registerDisplay(kDisplayId2,
+ std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+ kDisplay2Mode60->getId()));
+ ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2));
+
+ static constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+ mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
+
+ static constexpr bool kAllowToEnable = true;
+ mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
+}
+
+TEST_F(SchedulerTest, resyncAllLegacyAppliesToOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
+
+ // In the legacy code, prior to the flag, resync applied to OFF displays.
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
+ EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1);
+
+ mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+
+ mScheduler->registerDisplay(kDisplayId2,
+ std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+ kDisplay2Mode60->getId()));
+ ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2));
+
+ static constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
+ mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
+
+ static constexpr bool kAllowToEnable = true;
+ mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
}
class AttachedChoreographerTest : public SchedulerTest {
diff --git a/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp b/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
index 05f9eed..8615035 100644
--- a/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SmallAreaDetectionAllowMappingsTest.cpp
@@ -44,8 +44,8 @@
ASSERT_EQ(mMappings.getThresholdForAppId(kAppId2).value(), kThreshold2);
}
-TEST_F(SmallAreaDetectionAllowMappingsTest, testSetThesholdForAppId) {
- mMappings.setThesholdForAppId(kAppId1, kThreshold1);
+TEST_F(SmallAreaDetectionAllowMappingsTest, testSetThresholdForAppId) {
+ mMappings.setThresholdForAppId(kAppId1, kThreshold1);
ASSERT_EQ(mMappings.getThresholdForAppId(kAppId1), kThreshold1);
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index aeac80d..8b16a8a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -46,16 +46,20 @@
setupScheduler(selectorPtr);
- mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+ mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ DisplayHotplugEvent::CONNECTED);
mFlinger.configureAndCommit();
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(
TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, minFramePeriod())
+ .WillRepeatedly(Return(Period::fromNs(
+ TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)));
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
.setRefreshRateSelector(std::move(selectorPtr))
@@ -134,22 +138,25 @@
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(
Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, minFramePeriod())
+ .WillRepeatedly(Return(Period::fromNs(
+ TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
std::move(eventThread), std::move(sfEventThread),
std::move(selectorPtr),
TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp);
}
-TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
@@ -157,9 +164,9 @@
mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
120));
- ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -171,8 +178,9 @@
mFlinger.commit();
Mock::VerifyAndClearExpectations(mComposer);
- ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+
+ EXPECT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
@@ -182,14 +190,14 @@
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
}
-TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ EXPECT_FALSE(mDisplay->getDesiredMode());
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
@@ -197,9 +205,9 @@
mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
120));
- ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -214,8 +222,8 @@
mFlinger.commit();
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -224,8 +232,8 @@
// Test that if we call setDesiredDisplayModeSpecs while a previous mode change
// is still being processed the later call will be respected.
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
@@ -245,8 +253,8 @@
mock::createDisplayModeSpecs(kModeId120.value(), false, 0,
180));
- ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
EXPECT_CALL(*mComposer,
setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
@@ -255,20 +263,20 @@
mFlinger.commit();
- ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
mFlinger.commit();
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
}
-TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
@@ -276,9 +284,9 @@
mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
120));
- ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K);
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K);
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -312,18 +320,18 @@
// so we need to update with the new instance.
mDisplay = mFlinger.getDisplay(displayToken);
- ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
+ EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
- if (!arg->getDesiredActiveMode()) {
- *result_listener << "No desired active mode";
+ if (!arg->getDesiredMode()) {
+ *result_listener << "No desired mode";
return false;
}
- if (arg->getDesiredActiveMode()->modeOpt->modePtr->getId() != modeId) {
- *result_listener << "Unexpected desired active mode " << modeId;
+ if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) {
+ *result_listener << "Unexpected desired mode " << modeId;
return false;
}
@@ -336,9 +344,8 @@
}
MATCHER_P(ModeSettledTo, modeId, "") {
- if (const auto desiredOpt = arg->getDesiredActiveMode()) {
- *result_listener << "Unsettled desired active mode "
- << desiredOpt->modeOpt->modePtr->getId();
+ if (const auto desiredOpt = arg->getDesiredMode()) {
+ *result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId();
return false;
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 94d517a..b620830 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -110,7 +110,7 @@
EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), display.isPrimary());
std::optional<DisplayDeviceState::Physical> expectedPhysical;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ if (Case::Display::CONNECTION_TYPE::value) {
const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index ed8d909..844b96c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -188,5 +188,38 @@
scheduler.onHardwareVsyncRequest(mOuterDisplay->getPhysicalId(), true);
}
+TEST_F(FoldableTest, requestVsyncOnPowerOn) {
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true))
+ .Times(1);
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
+ .Times(1);
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+}
+
+TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) {
+ // When the device boots, the inner display should be the pacesetter.
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+
+ testing::InSequence seq;
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true))
+ .Times(1);
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
+ .Times(1);
+
+ // Turning off the pacesetter will result in disabling VSYNC.
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, false))
+ .Times(1);
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+
+ // Other display is now the pacesetter.
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 1210d0b..a270dc9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -27,10 +27,10 @@
EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2);
constexpr HWDisplayId hwcDisplayId1 = 456;
- mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED);
+ mFlinger.onComposerHalHotplugEvent(hwcDisplayId1, DisplayHotplugEvent::CONNECTED);
constexpr HWDisplayId hwcDisplayId2 = 654;
- mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED);
+ mFlinger.onComposerHalHotplugEvent(hwcDisplayId2, DisplayHotplugEvent::DISCONNECTED);
const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
ASSERT_EQ(2u, pendingEvents.size());
@@ -45,7 +45,7 @@
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
constexpr HWDisplayId displayId1 = 456;
- mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED);
+ mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED);
mFlinger.configure();
// The configure stage should consume the hotplug queue and produce a display transaction.
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index fc5f2b0..1583f64 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -46,7 +46,7 @@
EXPECT_CALL(static_cast<mock::VSyncTracker&>(
mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
- nextAnticipatedVSyncTimeFrom(_))
+ nextAnticipatedVSyncTimeFrom(_, _))
.WillRepeatedly(Return(0));
// --------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
new file mode 100644
index 0000000..7206e29
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class NotifyExpectedPresentTest : public DisplayTransactionTest {
+public:
+ void SetUp() override {
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject();
+ FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary)
+ .setPowerMode(hal::PowerMode::ON)
+ .inject(&mFlinger, mComposer);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+ static constexpr bool kIsPrimary = true;
+ static constexpr hal::HWDisplayId HWC_DISPLAY_ID =
+ FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+};
+
+TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) {
+ const auto physicDisplayId = mDisplay->getPhysicalId();
+ auto expectedPresentTime = systemTime() + ms2ns(10);
+ static constexpr Fps kFps60Hz = 60_Hz;
+ static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs();
+ static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.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(mFlinger.setNotifyExpectedPresentData(physicDisplayId,
+ kLastExpectedPresentTimestamp,
+ kFps60Hz));
+
+ {
+ // Very first ExpectedPresent after idle, no previous timestamp
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // Absent timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ /*timeoutOpt*/ std::nullopt);
+ }
+ {
+ // Timeout is 0
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ Period::fromNs(0));
+ }
+ {
+ // ExpectedPresent is after the timeoutNs
+ expectedPresentTime += 2 * kFrameInterval5HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent has not changed
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+ {
+ // ExpectedPresent is after the last reported ExpectedPresent.
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ 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 -= kFrameInterval120HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ kTimeoutNs);
+ }
+}
+
+TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
+ const auto physicDisplayId = mDisplay->getPhysicalId();
+ const auto now = systemTime();
+ auto expectedPresentTime = now;
+ static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
+
+ ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId,
+ 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(*mComposer,
+ notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
+ frameIntervalNs))
+ .WillOnce(Return(Error::NONE));
+ } else {
+ EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
+ }
+ mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime),
+ Fps::fromPeriodNsecs(frameIntervalNs),
+ kTimeoutNs);
+ }
+ }
+}
+} // namespace android
\ No newline at end of file
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/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index cf3fab3..15fe600 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -25,6 +25,11 @@
namespace android {
namespace {
+MATCHER_P(DisplayModeFps, value, "equals") {
+ using fps_approx_ops::operator==;
+ return arg->getVsyncRate() == value;
+}
+
// Used when we simulate a display that supports doze.
template <typename Display>
struct DozeIsSupportedVariant {
@@ -68,11 +73,13 @@
struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- setupVsyncNoCallExpectations(test);
+ EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- setupVsyncNoCallExpectations(test);
+ EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
};
@@ -94,7 +101,8 @@
static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule();
EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()),
- startPeriodTransition(DEFAULT_VSYNC_PERIOD, false))
+ onDisplayModeChanged(DisplayModeFps(Fps::fromPeriodNsecs(DEFAULT_VSYNC_PERIOD)),
+ false))
.Times(1);
EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel())
.Times(1);
@@ -292,6 +300,11 @@
// A sample configuration for the external display.
// In addition to not having event thread support, we emulate not having doze
// support.
+// TODO (b/267483230): ExternalDisplay supports the features tracked in
+// DispSyncIsSupportedVariant, but is the follower, so the
+// expectations set by DispSyncIsSupportedVariant don't match (wrong schedule).
+// We need a way to retrieve the proper DisplayId from
+// setupResetModelCallExpectations (or pass it in).
template <typename TransitionVariant>
using ExternalDisplayPowerCase =
DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp
new file mode 100644
index 0000000..e0b7366
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android::scheduler {
+
+TestableScheduler::TestableScheduler(RefreshRateSelectorPtr selectorPtr,
+ TestableSurfaceFlinger& testableSurfaceFlinger,
+ ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
+ std::make_shared<android::mock::VSyncTracker>(), std::move(selectorPtr),
+ testableSurfaceFlinger.getFactory(),
+ testableSurfaceFlinger.getTimeStats(), callback, vsyncTrackerCallback) {}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3d1c900..6213713 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,22 +32,27 @@
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
+namespace android {
+class TestableSurfaceFlinger;
+} // namespace android
+
namespace android::scheduler {
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
- : TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
- sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {}
+ TestableScheduler(RefreshRateSelectorPtr selectorPtr,
+ TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback,
+ IVsyncTrackerCallback& vsyncTrackerCallback);
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
- sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
- : Scheduler(*this, callback,
+ surfaceflinger::Factory& factory, TimeStats& timeStats,
+ ISchedulerCallback& schedulerCallback,
+ IVsyncTrackerCallback& vsyncTrackerCallback)
+ : Scheduler(*this, schedulerCallback,
(FeatureFlags)Feature::kContentDetection |
Feature::kSmallDirtyContentDetection,
- std::move(modulatorPtr)) {
+ factory, selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
std::move(tracker));
@@ -75,10 +80,11 @@
auto refreshRateSelector() { return pacesetterSelectorPtr(); }
- void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+ void registerDisplay(
+ PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ std::shared_ptr<VSyncTracker> vsyncTracker = std::make_shared<mock::VSyncTracker>()) {
registerDisplay(displayId, std::move(selectorPtr),
- std::make_unique<mock::VsyncController>(),
- std::make_shared<mock::VSyncTracker>());
+ std::make_unique<mock::VsyncController>(), vsyncTracker);
}
void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
@@ -111,6 +117,15 @@
Scheduler::setPacesetterDisplay(displayId);
}
+ std::optional<hal::PowerMode> getDisplayPowerMode(PhysicalDisplayId id) {
+ ftl::FakeGuard guard1(kMainThreadContext);
+ ftl::FakeGuard guard2(mDisplayLock);
+ return mDisplays.get(id).transform(
+ [](const Display& display) { return display.powerMode; });
+ }
+
+ using Scheduler::resyncAllToHardwareVsync;
+
auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
auto& mutableLayerHistory() { return mLayerHistory; }
auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8f1982d..f00eacc 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,18 @@
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) {
+ surfaceflinger::Factory& getFactory() { return mFactory; }
+
+ TimeStats& getTimeStats() { return *mFlinger->mTimeStats; }
+
+ 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) {
@@ -230,35 +238,34 @@
},
[](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; });
- const auto fps = selectorPtr->getActiveMode().fps;
- mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-
- mFlinger->mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
- hal::PowerMode::OFF);
-
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);
- auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
- mFlinger->mVsyncConfiguration->getCurrentConfigs());
+ using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback;
+ VsyncTrackerCallback& vsyncTrackerCallback =
+ vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp
+ ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback)
+ : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback);
if (useNiceMock) {
mScheduler =
new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
std::move(vsyncTracker),
std::move(selectorPtr),
- std::move(modulatorPtr),
- callback);
+ mFactory,
+ *mFlinger->mTimeStats,
+ schedulerCallback,
+ vsyncTrackerCallback);
} else {
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker),
- std::move(selectorPtr),
- std::move(modulatorPtr), callback);
+ std::move(selectorPtr), mFactory,
+ *mFlinger->mTimeStats, schedulerCallback,
+ vsyncTrackerCallback);
}
mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
@@ -271,7 +278,7 @@
resetScheduler(mScheduler);
}
- void setupMockScheduler(test::MockSchedulerOptions options = {}) {
+ void setupMockScheduler(surfaceflinger::test::MockSchedulerOptions options = {}) {
using testing::_;
using testing::Return;
@@ -291,13 +298,17 @@
auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0));
EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
- EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, minFramePeriod())
+ .WillRepeatedly(
+ Return(Period::fromNs(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)));
+ 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); }
@@ -375,13 +386,14 @@
LOG_ALWAYS_FATAL_IF(!displayIdOpt);
const auto displayId = *displayIdOpt;
- constexpr bool kBackpressureGpuComposition = true;
- scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition);
+ scheduler::FrameTargeter frameTargeter(displayId,
+ scheduler::Feature::kBackpressureGpuComposition);
frameTargeter.beginFrame({.frameBeginTime = frameTime,
.vsyncId = vsyncId,
.expectedVsyncTime = expectedVsyncTime,
- .sfWorkDuration = 10ms},
+ .sfWorkDuration = 10ms,
+ .hwcMinWorkDuration = 10ms},
*mScheduler->getVsyncSchedule());
scheduler::FrameTargets targets;
@@ -450,8 +462,8 @@
mFlinger->commitTransactionsLocked(transactionFlags);
}
- void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
- mFlinger->onComposerHalHotplug(hwcDisplayId, connection);
+ void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) {
+ mFlinger->onComposerHalHotplugEvent(hwcDisplayId, event);
}
auto setDisplayStateLocked(const DisplayState& s) {
@@ -477,12 +489,13 @@
auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- bool forSystem, bool regionSampling) {
+ bool regionSampling) {
ScreenCaptureResults captureResults;
return FTL_FAKE_GUARD(kMainThreadContext,
mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers,
- buffer, forSystem, regionSampling,
- false /* grayscale */, captureResults));
+ buffer, regionSampling,
+ false /* grayscale */,
+ false /* isProtected */, captureResults));
}
auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
@@ -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);
@@ -675,6 +691,21 @@
mFlinger->mLegacyFrontEndEnabled = false;
}
+ void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod,
+ TimePoint expectedPresentTime, Fps frameInterval,
+ std::optional<Period> timeoutOpt) {
+ mFlinger->notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime,
+ frameInterval, timeoutOpt);
+ }
+
+ void setNotifyExpectedPresentData(PhysicalDisplayId displayId,
+ TimePoint lastExpectedPresentTimestamp,
+ Fps lastFrameInterval) {
+ auto& displayData = mFlinger->mNotifyExpectedPresentMap[displayId];
+ displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp;
+ displayData.lastFrameInterval = lastFrameInterval;
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may
@@ -1071,6 +1102,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..d071ce9 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,35 @@
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();
+}
+
+// Check we cannot write to file if the trace write is disabled.
+TEST_F(TransactionTraceWriterTest, canDisableTraceWriter) {
+ TransactionTraceWriter::getInstance().disable();
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
+ EXPECT_NE(access(mFilename.c_str(), F_OK), 0);
+
+ TransactionTraceWriter::getInstance().enable();
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
+ EXPECT_EQ(access(mFilename.c_str(), F_OK), 0);
+ verifyTraceFile();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 7981224..fb4ef70 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -214,6 +214,7 @@
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).layer_id(), mChildLayerId);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
@@ -224,6 +225,7 @@
perfetto::protos::TransactionTraceFile proto = writeToProto();
// verify starting states are updated correctly
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
@@ -235,6 +237,7 @@
// verify the child layer has been removed from the trace
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
@@ -254,6 +257,7 @@
// verify we still have the parent layer state
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+ EXPECT_TRUE(proto.entry(0).displays_changed());
}
class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 41866a1..d891008 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -34,40 +34,50 @@
return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count();
}
-class FixedRateIdealStubTracker : public VSyncTracker {
+class StubTracker : public VSyncTracker {
public:
- FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
+ StubTracker(nsecs_t period) : mPeriod(period) {}
bool addVsyncTimestamp(nsecs_t) final { return true; }
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+ nsecs_t currentPeriod() const final {
+ std::lock_guard lock(mMutex);
+ return mPeriod;
+ }
+
+ Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); }
+ void resetModel() final {}
+ bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
+ void setRenderRate(Fps) final {}
+ void onFrameBegin(TimePoint, TimePoint) final {}
+ void onFrameMissed(TimePoint) final {}
+ void dump(std::string&) const final {}
+
+protected:
+ std::mutex mutable mMutex;
+ nsecs_t mPeriod;
+};
+
+class FixedRateIdealStubTracker : public StubTracker {
+public:
+ FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {}
+
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) const final {
auto const floor = timePoint % mPeriod;
if (floor == 0) {
return timePoint;
}
return timePoint - floor + mPeriod;
}
-
- nsecs_t currentPeriod() const final { return mPeriod; }
-
- void setPeriod(nsecs_t) final {}
- void resetModel() final {}
- bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
- void setRenderRate(Fps) final {}
- void dump(std::string&) const final {}
-
-private:
- nsecs_t const mPeriod;
};
-class VRRStubTracker : public VSyncTracker {
+class VRRStubTracker : public StubTracker {
public:
- VRRStubTracker(nsecs_t period) : mPeriod{period} {}
+ VRRStubTracker(nsecs_t period) : StubTracker(period) {}
- bool addVsyncTimestamp(nsecs_t) final { return true; }
-
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) const final {
std::lock_guard lock(mMutex);
auto const normalized_to_base = time_point - mBase;
auto const floor = (normalized_to_base) % mPeriod;
@@ -83,21 +93,7 @@
mBase = last_known;
}
- nsecs_t currentPeriod() const final {
- std::lock_guard lock(mMutex);
- return mPeriod;
- }
-
- void setPeriod(nsecs_t) final {}
- void resetModel() final {}
- bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
- void setRenderRate(Fps) final {}
- void dump(std::string&) const final {}
-
private:
- std::mutex mutable mMutex;
- nsecs_t mPeriod;
nsecs_t mBase = 0;
};
@@ -121,7 +117,7 @@
mCallback.schedule(
{.workDuration = mWorkload,
.readyDuration = mReadyDuration,
- .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
+ .lastVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
for (auto i = 0u; i < iterations - 1; i++) {
std::unique_lock lock(mMutex);
@@ -134,7 +130,7 @@
mCallback.schedule({.workDuration = mWorkload,
.readyDuration = mReadyDuration,
- .earliestVsync = last + mWorkload + mReadyDuration});
+ .lastVsync = last + mWorkload + mReadyDuration});
}
// wait for the last callback.
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 1dc5498..4bf58de 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -30,9 +30,10 @@
#include <scheduler/TimeKeeper.h>
-#include "FlagUtils.h"
+#include <common/test/FlagUtils.h>
#include "Scheduler/VSyncDispatchTimerQueue.h"
#include "Scheduler/VSyncTracker.h"
+#include "mock/MockVSyncTracker.h"
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -42,27 +43,17 @@
namespace android::scheduler {
using namespace com::android::graphics::surfaceflinger;
-class MockVSyncTracker : public VSyncTracker {
+class MockVSyncTracker : public mock::VSyncTracker {
public:
MockVSyncTracker(nsecs_t period) : mPeriod{period} {
- ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
+ ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_, _))
.WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
ON_CALL(*this, currentPeriod())
.WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod));
}
- MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
- MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
- MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
- MOCK_METHOD1(setPeriod, void(nsecs_t));
- MOCK_METHOD0(resetModel, void());
- MOCK_CONST_METHOD0(needsMoreSamples, bool());
- MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
- MOCK_CONST_METHOD1(dump, void(std::string&));
-
- nsecs_t nextVSyncTime(nsecs_t timePoint) const {
+ nsecs_t nextVSyncTime(nsecs_t timePoint, std::optional<nsecs_t>) const {
if (timePoint % mPeriod == 0) {
return timePoint;
}
@@ -252,10 +243,9 @@
mDispatchGroupThreshold,
mVsyncMoveThreshold);
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 1000});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -266,10 +256,9 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
@@ -286,16 +275,14 @@
EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
CountingCallback cb(mDispatch);
- auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = intended});
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
result =
- mDispatch->update(cb,
- {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+ mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(700, *result);
@@ -312,17 +299,18 @@
CountingCallback cb(mDispatch);
const auto result =
- mDispatch->update(cb,
- {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+ mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
EXPECT_FALSE(result.has_value());
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(mPeriod)))
+ .WillOnce(Return(1150));
EXPECT_CALL(mMockClock, alarmAt(_, 1050));
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -333,7 +321,8 @@
auto const now = 234;
mMockClock.advanceBy(234);
auto const workDuration = 10 * mPeriod;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(now + workDuration, std::optional<nsecs_t>(mPeriod)))
.WillOnce(Return(mPeriod * 11));
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
@@ -341,7 +330,7 @@
const auto result = mDispatch->schedule(cb,
{.workDuration = workDuration,
.readyDuration = 0,
- .earliestVsync = mPeriod});
+ .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod, *result);
}
@@ -351,10 +340,9 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
@@ -365,10 +353,9 @@
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
mMockClock.advanceBy(950);
@@ -380,10 +367,9 @@
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
@@ -402,10 +388,9 @@
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = mPeriod});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod - 100, *result);
@@ -422,7 +407,8 @@
}
TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.Times(4)
.WillOnce(Return(1055))
.WillOnce(Return(1063))
@@ -437,8 +423,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
- mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
+ mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod});
advanceToNextCallback();
advanceToNextCallback();
@@ -450,7 +436,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(4)
.WillOnce(Return(1000))
.WillOnce(Return(2000))
@@ -464,21 +450,21 @@
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 0});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
EXPECT_THAT(cb.mCalls[0], Eq(1000));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(2));
EXPECT_THAT(cb.mCalls[1], Eq(2000));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
advanceToNextCallback();
@@ -487,7 +473,7 @@
}
TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(4)
.WillOnce(Return(10000))
.WillOnce(Return(1000))
@@ -502,9 +488,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
- mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod * 10});
+ mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .lastVsync = mPeriod});
mDispatch->cancel(cb1);
}
@@ -516,9 +501,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
}
@@ -531,9 +516,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
}
@@ -551,10 +536,9 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1,
- {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = closeOffset, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -562,11 +546,9 @@
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
- mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .lastVsync = 2000});
mDispatch->schedule(cb1,
- {.workDuration = notCloseOffset,
- .readyDuration = 0,
- .earliestVsync = 2000});
+ {.workDuration = notCloseOffset, .readyDuration = 0, .lastVsync = 2000});
advanceToNextCallback();
ASSERT_THAT(cb1.mCalls.size(), Eq(2));
EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -586,32 +568,32 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled);
}
TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(3)
.WillOnce(Return(950))
.WillOnce(Return(1975))
.WillOnce(Return(2950));
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 920});
mMockClock.advanceBy(850);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1900});
mMockClock.advanceBy(900);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
mMockClock.advanceBy(125);
EXPECT_THAT(cb.mCalls.size(), Eq(2));
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2900});
mMockClock.advanceBy(975);
EXPECT_THAT(cb.mCalls.size(), Eq(3));
}
@@ -625,13 +607,11 @@
tmp = mDispatch->registerCallback(
[&](auto, auto, auto) {
mDispatch->schedule(tmp,
- {.workDuration = 100,
- .readyDuration = 0,
- .earliestVsync = 2000});
+ {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
},
"o.o");
- mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
}
@@ -640,30 +620,29 @@
std::optional<nsecs_t> lastTarget;
tmp = mDispatch->registerCallback(
[&](auto timestamp, auto, auto) {
- auto result =
- mDispatch->schedule(tmp,
- {.workDuration = 400,
- .readyDuration = 0,
- .earliestVsync = timestamp - mVsyncMoveThreshold});
+ auto result = mDispatch->schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .lastVsync = timestamp - mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
result = mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
- .earliestVsync = timestamp});
+ .lastVsync = timestamp});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
result = mDispatch->schedule(tmp,
{.workDuration = 400,
.readyDuration = 0,
- .earliestVsync = timestamp + mVsyncMoveThreshold});
+ .lastVsync = timestamp + mVsyncMoveThreshold});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(mPeriod + timestamp - 400, *result);
lastTarget = timestamp;
},
"oo");
- mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
EXPECT_THAT(lastTarget, Eq(1000));
@@ -679,16 +658,16 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .lastVsync = 1000});
mMockClock.advanceBy(750);
- mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
- mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .lastVsync = 2000});
mMockClock.advanceBy(800);
- mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
}
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
@@ -701,12 +680,12 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
- mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
- mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .lastVsync = 2000});
+ mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
advanceToNextCallback();
@@ -718,8 +697,8 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .lastVsync = 20000});
}
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
@@ -729,17 +708,15 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
CountingCallback cb0(mDispatch);
- mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
mDispatch->cancel(cb0);
- mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
}
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
EXPECT_FALSE(
- mDispatch
- ->schedule(token,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+ mDispatch->schedule(token, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000})
.has_value());
EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error));
}
@@ -747,12 +724,10 @@
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
auto result =
- mDispatch->schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
- result = mDispatch->schedule(cb0,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -764,14 +739,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
@@ -783,17 +756,17 @@
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,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(400, *result);
@@ -802,19 +775,18 @@
}
TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.Times(2)
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(400);
- result = mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(602, *result);
}
@@ -822,13 +794,12 @@
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
auto result =
- mDispatch->schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
advanceToNextCallback();
- result = mDispatch->schedule(cb0,
- {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+ result =
+ mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
}
@@ -839,13 +810,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
auto result =
- mDispatch->schedule(cb0,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
advanceToNextCallback();
- result = mDispatch->schedule(cb0,
- {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+ result =
+ mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1100, *result);
}
@@ -857,13 +827,11 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
@@ -873,17 +841,17 @@
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 =
- mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb,
- {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(0, *result);
@@ -897,10 +865,10 @@
VSyncCallbackRegistration cb(
mDispatch, [](auto, auto, auto) {}, "");
VSyncCallbackRegistration cb1(std::move(cb));
- cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
cb.cancel();
- cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
cb1.cancel();
}
@@ -913,10 +881,10 @@
VSyncCallbackRegistration cb1(
mDispatch, [](auto, auto, auto) {}, "");
cb1 = std::move(cb);
- cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
cb.cancel();
- cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
cb1.cancel();
}
@@ -929,16 +897,14 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- result = mDispatch->schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
mMockClock.advanceBy(80);
@@ -957,16 +923,14 @@
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- result = mDispatch->schedule(cb,
- {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1630, *result);
mMockClock.advanceBy(80);
@@ -983,12 +947,10 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
@@ -1012,12 +974,10 @@
CountingCallback cb2(mDispatch);
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb2,
- {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+ result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1900, *result);
@@ -1039,21 +999,21 @@
CountingCallback cb2(mDispatch);
Sequence seq;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.InSequence(seq)
.WillOnce(Return(1000));
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000, std::optional<nsecs_t>(1000)))
.InSequence(seq)
.WillOnce(Return(1000));
auto result =
- mDispatch->schedule(cb1,
- {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(600, *result);
- result = mDispatch->schedule(cb2,
- {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(610, *result);
@@ -1075,10 +1035,9 @@
EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- const auto result = mDispatch->schedule(cb,
- {.workDuration = 70,
- .readyDuration = 30,
- .earliestVsync = intended});
+ const auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 70, .readyDuration = 30, .lastVsync = intended});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(900, *result);
advanceToNextCallback();
@@ -1099,8 +1058,8 @@
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
@@ -1119,11 +1078,12 @@
Sequence seq;
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq);
CountingCallback cb(mDispatch);
- mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
- mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
advanceToNextCallback();
@@ -1132,7 +1092,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));
}
@@ -1143,14 +1103,12 @@
EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
auto result =
- mDispatch->schedule(cb,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(300);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(1200, *result);
@@ -1161,22 +1119,27 @@
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,
- {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(500, *result);
mMockClock.advanceBy(300);
- result = mDispatch->schedule(cb,
- {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
EXPECT_TRUE(result.has_value());
EXPECT_EQ(300, *result);
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 {
@@ -1201,7 +1164,7 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
@@ -1216,14 +1179,15 @@
auto const duration = 500;
auto const now = 8750;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(now + duration, std::optional<nsecs_t>(994)))
.Times(1)
.WillOnce(Return(10000));
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+ EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
*mStubTracker.get(), now)
.has_value());
auto const wakeup = entry.wakeupTime();
@@ -1246,7 +1210,7 @@
},
mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
@@ -1269,7 +1233,7 @@
}
TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_, _))
.Times(2)
.WillOnce(Return(1000))
.WillOnce(Return(1020));
@@ -1281,7 +1245,7 @@
entry.update(*mStubTracker.get(), 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto wakeup = entry.wakeupTime();
@@ -1297,7 +1261,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
entry.update(*mStubTracker.get(), 0);
@@ -1310,24 +1274,24 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
*mStubTracker.get(), 0)
.has_value());
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
@@ -1340,23 +1304,25 @@
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
Sequence seq;
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500)))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
+ EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500, std::optional<nsecs_t>(500)))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+ EXPECT_CALL(*mStubTracker.get(),
+ nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold,
+ std::optional<nsecs_t>(1000)))
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
entry.executing(); // 1000 is executing
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
}
@@ -1364,16 +1330,16 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
- EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
}
@@ -1383,9 +1349,9 @@
VSyncDispatchTimerQueueEntry entry(
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
- entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400});
+ entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .lastVsync = 400});
entry.addPendingWorkloadUpdate(
- {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
+ {.workDuration = effectualOffset, .readyDuration = 0, .lastVsync = 400});
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
entry.update(*mStubTracker.get(), 0);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
@@ -1407,7 +1373,7 @@
},
mVsyncMoveThreshold);
- EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+ EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
*mStubTracker.get(), 0)
.has_value());
auto const wakeup = entry.wakeupTime();
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 43d683d..961ba57 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -23,7 +23,10 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0
+#include <common/test/FlagUtils.h>
#include "Scheduler/VSyncPredictor.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockVsyncTrackerCallback.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -31,15 +34,31 @@
#include <chrono>
#include <utility>
+#include <com_android_graphics_surfaceflinger_flags.h>
+
using namespace testing;
using namespace std::literals;
+using namespace com::android::graphics::surfaceflinger;
+
+using NotifyExpectedPresentConfig =
+ ::aidl::android::hardware::graphics::composer3::VrrConfig::NotifyExpectedPresentConfig;
+
+using android::mock::createDisplayMode;
+using android::mock::createDisplayModeBuilder;
+using android::mock::createVrrDisplayMode;
namespace android::scheduler {
+namespace {
MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") {
return arg <= value + tolerance && arg >= value - tolerance;
}
+MATCHER_P(FpsMatcher, value, "equals") {
+ using fps_approx_ops::operator==;
+ return arg == value;
+}
+
std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) {
std::vector<nsecs_t> vsyncs(count);
std::generate(vsyncs.begin(), vsyncs.end(),
@@ -49,16 +68,27 @@
constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+ftl::NonNull<DisplayModePtr> displayMode(nsecs_t period) {
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(period);
+ return ftl::as_non_null(createDisplayMode(DisplayModeId(0), refreshRate, kGroup, kResolution,
+ DEFAULT_DISPLAY_ID));
+}
+} // namespace
+
struct VSyncPredictorTest : testing::Test {
nsecs_t mNow = 0;
nsecs_t mPeriod = 1000;
+ ftl::NonNull<DisplayModePtr> mMode = displayMode(mPeriod);
+ 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{mMode, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent, mVsyncTrackerCallback};
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
@@ -68,7 +98,7 @@
EXPECT_THAT(model.intercept, Eq(0));
auto const changedPeriod = 2000;
- tracker.setPeriod(changedPeriod);
+ tracker.setDisplayModePtr(displayMode(changedPeriod));
model = tracker.getVSyncPredictionModel();
EXPECT_THAT(model.slope, Eq(changedPeriod));
EXPECT_THAT(model.intercept, Eq(0));
@@ -89,7 +119,7 @@
EXPECT_FALSE(tracker.needsMoreSamples());
auto const changedPeriod = mPeriod * 2;
- tracker.setPeriod(changedPeriod);
+ tracker.setDisplayModePtr(displayMode(changedPeriod));
EXPECT_TRUE(tracker.needsMoreSamples());
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
@@ -123,7 +153,7 @@
}
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyLessPeriod));
- tracker.setPeriod(changedPeriod);
+ tracker.setDisplayModePtr(displayMode(changedPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod));
}
@@ -169,7 +199,7 @@
auto constexpr expectedPeriod = 16639242;
auto constexpr expectedIntercept = 1049341;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -188,7 +218,7 @@
auto expectedPeriod = 11089413;
auto expectedIntercept = 94421;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -215,7 +245,7 @@
auto expectedPeriod = 45450152;
auto expectedIntercept = 469647;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -241,7 +271,7 @@
auto expectedPeriod = 1999892;
auto expectedIntercept = 86342;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -261,7 +291,7 @@
auto const simulatedVsyncsSlow =
generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
- tracker.setPeriod(fastPeriod);
+ tracker.setDisplayModePtr(displayMode(fastPeriod));
for (auto const& timestamp : simulatedVsyncsFast) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -271,7 +301,7 @@
EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError));
EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
- tracker.setPeriod(slowPeriod);
+ tracker.setDisplayModePtr(displayMode(slowPeriod));
for (auto const& timestamp : simulatedVsyncsSlow) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -295,7 +325,7 @@
generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod2, fastTimeBase);
auto idealPeriod = 100000;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncsFast) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -303,14 +333,14 @@
EXPECT_THAT(model.slope, Eq(fastPeriod));
EXPECT_THAT(model.intercept, Eq(0));
- tracker.setPeriod(slowPeriod);
+ tracker.setDisplayModePtr(displayMode(slowPeriod));
for (auto const& timestamp : simulatedVsyncsSlow) {
tracker.addVsyncTimestamp(timestamp);
}
// we had a model for 100ns mPeriod before, use that until the new samples are
// sufficiently built up
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
model = tracker.getVSyncPredictionModel();
EXPECT_THAT(model.slope, Eq(fastPeriod));
EXPECT_THAT(model.intercept, Eq(0));
@@ -359,7 +389,7 @@
auto const expectedPeriod = 11113919;
auto const expectedIntercept = -1195945;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -378,8 +408,9 @@
// See b/151146131
TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
- VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction,
- kOutlierTolerancePercent};
+ const auto mode = displayMode(mPeriod);
+ VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent,
+ mVsyncTrackerCallback};
std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
840923581635, 840940161584, 840956868096,
840973702473, 840990256277, 841007116851,
@@ -393,7 +424,7 @@
auto const expectedPeriod = 16698426;
auto const expectedIntercept = 58055;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -406,7 +437,7 @@
TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
auto const idealPeriod = 10000;
auto const realPeriod = 10500;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
tracker.addVsyncTimestamp(i * realPeriod);
}
@@ -548,7 +579,7 @@
auto constexpr expectedPeriod = 16'644'742;
auto constexpr expectedIntercept = 125'626;
- tracker.setPeriod(idealPeriod);
+ tracker.setDisplayModePtr(displayMode(idealPeriod));
for (auto const& timestamp : simulatedVsyncs) {
tracker.addVsyncTimestamp(timestamp);
}
@@ -626,6 +657,82 @@
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);
+ NotifyExpectedPresentConfig notifyExpectedPresentConfig;
+ notifyExpectedPresentConfig.timeoutNs = Period::fromNs(30).ns();
+
+ hal::VrrConfig vrrConfig;
+ vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig;
+ vrrConfig.minFrameIntervalNs = refreshRate.getPeriodNsecs();
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID));
+
+ tracker.setDisplayModePtr(mode);
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_CALL(mVsyncTrackerCallback,
+ onVsyncGenerated(TimePoint::fromNs(last + mPeriod), mode,
+ FpsMatcher(refreshRate)))
+ .Times(1);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+
+ tracker.setRenderRate(refreshRate / 2);
+ {
+ // out of render rate phase
+ EXPECT_CALL(mVsyncTrackerCallback,
+ onVsyncGenerated(TimePoint::fromNs(mNow + 3 * mPeriod), mode,
+ FpsMatcher(refreshRate / 2)))
+ .Times(1);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod),
+ Eq(mNow + 3 * mPeriod));
+ }
+}
+
+TEST_F(VSyncPredictorTest, adjustsVrrTimeline) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction,
+ kOutlierTolerancePercent, mVsyncTrackerCallback};
+
+ vrrTracker.setRenderRate(minFrameRate);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
+
+ vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+ EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 2000));
+ EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(3500, 3500));
+
+ // Miss when starting 4500 and expect the next vsync will be at 5000 (next one)
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3500), TimePoint::fromNs(2500));
+ vrrTracker.onFrameMissed(TimePoint::fromNs(4500));
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+}
+
} // 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..8d9623d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -31,6 +31,9 @@
#include <scheduler/TimeKeeper.h>
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockVSyncTracker.h"
+
#include "Scheduler/VSyncDispatch.h"
#include "Scheduler/VSyncReactor.h"
#include "Scheduler/VSyncTracker.h"
@@ -40,20 +43,7 @@
namespace android::scheduler {
-class MockVSyncTracker : public VSyncTracker {
-public:
- MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
- MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
- MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
- MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
- MOCK_METHOD1(setPeriod, void(nsecs_t));
- MOCK_METHOD0(resetModel, void());
- MOCK_CONST_METHOD0(needsMoreSamples, bool());
- MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
- MOCK_CONST_METHOD1(dump, void(std::string&));
-};
-
+namespace {
class MockClock : public Clock {
public:
MOCK_CONST_METHOD0(now, nsecs_t());
@@ -93,18 +83,33 @@
constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+ftl::NonNull<DisplayModePtr> displayMode(nsecs_t vsyncPeriod) {
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(vsyncPeriod);
+ return ftl::as_non_null(mock::createDisplayMode(DisplayModeId(0), refreshRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID));
+}
+
+MATCHER_P(DisplayModeMatcher, value, "display mode equals") {
+ return arg->getId() == value->getId() && equalsExceptDisplayModeId(*arg, *value);
+}
+
+} // namespace
+
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
- : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
+ : mMockTracker(std::make_shared<NiceMock<mock::VSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
mReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
kPendingLimit, false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
+ ON_CALL(*mMockTracker, addVsyncTimestamp(_)).WillByDefault(Return(true));
}
- std::shared_ptr<MockVSyncTracker> mMockTracker;
+ std::shared_ptr<mock::VSyncTracker> mMockTracker;
std::shared_ptr<MockClock> mMockClock;
static constexpr size_t kPendingLimit = 3;
static constexpr nsecs_t mDummyTime = 47;
@@ -194,7 +199,8 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod, false);
+
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -206,8 +212,8 @@
TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
nsecs_t const newPeriod = 5000;
- EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.startPeriodTransition(newPeriod, false);
+ EXPECT_CALL(*mMockTracker, setDisplayModePtr(_)).Times(0);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
@@ -217,7 +223,7 @@
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
- EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+ EXPECT_CALL(*mMockTracker, setDisplayModePtr(/*displayMode(newPeriod)*/ _)).Times(1);
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
@@ -226,7 +232,7 @@
TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
nsecs_t sampleTime = 0;
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -234,7 +240,7 @@
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.startPeriodTransition(period, false);
+ mReactor.onDisplayModeChanged(displayMode(period), false);
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -244,13 +250,13 @@
nsecs_t const secondPeriod = 5000;
nsecs_t const thirdPeriod = 2000;
- mReactor.startPeriodTransition(secondPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(secondPeriod), false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.startPeriodTransition(thirdPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(thirdPeriod), false);
EXPECT_TRUE(
mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -291,21 +297,22 @@
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
nsecs_t const newPeriod = 5000;
- EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.startPeriodTransition(newPeriod, false);
+ EXPECT_CALL(*mMockTracker, setDisplayModePtr(_)).Times(0);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
- EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+ EXPECT_CALL(*mMockTracker, setDisplayModePtr(DisplayModeMatcher(displayMode(newPeriod))))
+ .Times(1);
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -323,7 +330,7 @@
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
auto time = 0;
auto constexpr numTimestampSubmissions = 10;
@@ -348,7 +355,7 @@
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
auto time = 0;
// If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
@@ -365,7 +372,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
time += period;
mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
@@ -381,7 +388,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
static auto constexpr numSamplesWithNewPeriod = 4;
Sequence seq;
@@ -408,7 +415,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -428,7 +435,7 @@
nsecs_t const newPeriod1 = 4000;
nsecs_t const newPeriod2 = 7000;
- mReactor.startPeriodTransition(newPeriod1, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod1), false);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -447,7 +454,7 @@
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
- mReactor.startPeriodTransition(newPeriod2, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod2), false);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
@@ -460,7 +467,7 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.startPeriodTransition(newPeriod, false);
+ mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -484,7 +491,7 @@
// First, set the same period, which should only be confirmed when we receive two
// matching callbacks
- idleReactor.startPeriodTransition(10000, false);
+ idleReactor.onDisplayModeChanged(displayMode(10000), false);
EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period but incorrect timestamp delta
@@ -497,7 +504,7 @@
// Then, set a new period, which should be confirmed as soon as we receive a callback
// reporting the new period
nsecs_t const newPeriod = 5000;
- idleReactor.startPeriodTransition(newPeriod, false);
+ idleReactor.onDisplayModeChanged(displayMode(newPeriod), false);
// Incorrect timestamp delta and period
EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
index a8a3cd0..bfdd596 100644
--- a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -25,10 +25,12 @@
#include <scheduler/Fps.h>
#include "Scheduler/VsyncSchedule.h"
#include "ThreadContext.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
+using android::mock::createDisplayMode;
using testing::_;
namespace android {
@@ -157,35 +159,35 @@
// allowed.
ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
- const Period period = (60_Hz).getPeriod();
+ const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz));
EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
- EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
+ EXPECT_CALL(getController(), onDisplayModeChanged(mode, false));
- mVsyncSchedule->startPeriodTransition(period, false);
+ mVsyncSchedule->onDisplayModeChanged(mode, false);
}
TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) {
ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
mVsyncSchedule->enableHardwareVsync();
- const Period period = (60_Hz).getPeriod();
+ const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz));
EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0);
- EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
+ EXPECT_CALL(getController(), onDisplayModeChanged(mode, false));
- mVsyncSchedule->startPeriodTransition(period, false);
+ mVsyncSchedule->onDisplayModeChanged(mode, false);
}
TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) {
ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
- const Period period = (60_Hz).getPeriod();
+ const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz));
EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true));
- EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true));
+ EXPECT_CALL(getController(), onDisplayModeChanged(mode, true));
- mVsyncSchedule->startPeriodTransition(period, true);
+ mVsyncSchedule->onDisplayModeChanged(mode, true);
}
TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 0b07745..184dada 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -51,7 +51,7 @@
~Composer() override;
MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
- MOCK_METHOD(bool, getDisplayConfigurationsSupported, (), (const, override));
+ MOCK_METHOD(bool, isVrrSupported, (), (const, override));
MOCK_METHOD0(getCapabilities,
std::vector<aidl::android::hardware::graphics::composer3::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
@@ -86,18 +86,19 @@
MOCK_METHOD3(getReleaseFences, Error(Display, std::vector<Layer>*, std::vector<int>*));
MOCK_METHOD2(presentDisplay, Error(Display, int*));
MOCK_METHOD2(setActiveConfig, Error(Display, Config));
- MOCK_METHOD6(setClientTarget,
- Error(Display, uint32_t, const sp<GraphicBuffer>&, int, Dataspace,
- const std::vector<IComposerClient::Rect>&));
+ MOCK_METHOD(Error, setClientTarget,
+ (Display, uint32_t, const sp<GraphicBuffer>&, int, Dataspace,
+ const std::vector<IComposerClient::Rect>&, float),
+ (override));
MOCK_METHOD3(setColorMode, Error(Display, ColorMode, RenderIntent));
MOCK_METHOD2(setColorTransform, Error(Display, const float*));
MOCK_METHOD3(setOutputBuffer, Error(Display, const native_handle_t*, int));
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/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index cb05c00..5bcce50 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -52,6 +52,7 @@
.setVrrConfig(std::move(vrrConfig))
.build();
}
+
inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
return DisplayMode::Builder(modePtr->getHwcId())
.setId(modePtr->getId())
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 40f59b8..7413235 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -66,8 +66,8 @@
((std::unordered_map<Layer *, android::sp<android::Fence>> *)), (const, override));
MOCK_METHOD(hal::Error, present, (android::sp<android::Fence> *), (override));
MOCK_METHOD(hal::Error, setClientTarget,
- (uint32_t, const android::sp<android::GraphicBuffer> &,
- const android::sp<android::Fence> &, hal::Dataspace),
+ (uint32_t, const android::sp<android::GraphicBuffer>&,
+ const android::sp<android::Fence>&, hal::Dataspace, float),
(override));
MOCK_METHOD(hal::Error, setColorMode, (hal::ColorMode, hal::RenderIntent), (override));
MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
@@ -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/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
index a088aab..ed1405b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
@@ -18,12 +18,21 @@
#include "binder/Status.h"
+// FMQ library in IPower does questionable conversions
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
+#pragma clang diagnostic pop
+
#include <gmock/gmock.h>
using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::ChannelConfig;
using aidl::android::hardware::power::IPower;
using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionTag;
+
using aidl::android::hardware::power::Mode;
using android::binder::Status;
@@ -42,6 +51,14 @@
int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
(override));
MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
+ (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos, SessionTag tag, SessionConfig* config,
+ std::shared_ptr<IPowerHintSession>* _aidl_return),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
+ (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 364618d..27564b2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -18,10 +18,15 @@
#include "binder/Status.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
+#pragma clang diagnostic pop
+
#include <gmock/gmock.h>
using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::SessionMode;
using android::binder::Status;
@@ -47,6 +52,7 @@
MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, getSessionConfig, (SessionConfig * _aidl_return), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index d635508..8e8eb1d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,11 +36,10 @@
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
- MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(void, reportActualWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
- MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
+ MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
MOCK_METHOD(void, setGpuFenceTime,
(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
MOCK_METHOD(void, setHwcValidateTiming,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index 68fe3c5..b17c8ad 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -19,7 +19,10 @@
#include <gmock/gmock.h>
#include <scheduler/Time.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
#include <powermanager/PowerHalController.h>
+#pragma clang diagnostic pop
namespace android {
namespace hardware {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 866af3b..e2b0ed1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -59,6 +59,9 @@
MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
MOCK_METHOD(void, pauseVsyncCallback, (bool));
MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
+ MOCK_METHOD(void, onHdcpLevelsChanged,
+ (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
+ (override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 4cc78fe..4204aa0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -17,6 +17,7 @@
#pragma once
#include <gmock/gmock.h>
+#include <optional>
namespace android::mock {
@@ -27,6 +28,13 @@
EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
.WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
}
+
+ MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {
+ EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
+ .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
+ }
+
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
@@ -37,6 +45,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.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index bcccae5..cc24f42 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -17,9 +17,13 @@
#include "mock/MockVSyncTracker.h"
namespace android::mock {
+using testing::Return;
// Explicit default instantiation is recommended.
-VSyncTracker::VSyncTracker() = default;
VSyncTracker::~VSyncTracker() = default;
+VSyncTracker::VSyncTracker() {
+ ON_CALL(*this, minFramePeriod()).WillByDefault(Return(Period::fromNs(0)));
+}
+
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index dcf25e1..3870983 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -27,15 +27,19 @@
VSyncTracker();
~VSyncTracker() override;
- MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
- MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
- MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
- MOCK_METHOD1(setPeriod, void(nsecs_t));
- MOCK_METHOD0(resetModel, void());
- MOCK_CONST_METHOD0(needsMoreSamples, bool());
- MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override));
+ MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>),
+ (const, override));
+ MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override));
+ MOCK_METHOD(Period, minFramePeriod, (), (const, override));
+ MOCK_METHOD(void, resetModel, (), (override));
+ MOCK_METHOD(bool, needsMoreSamples, (), (const, override));
+ MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (const, override));
+ MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
MOCK_METHOD(void, setRenderRate, (Fps), (override));
- MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));
+ MOCK_METHOD(void, onFrameMissed, (TimePoint), (override));
+ MOCK_METHOD(void, dump, (std::string&), (const, override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 69ec60a..f743390 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -29,7 +29,7 @@
MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override));
MOCK_METHOD(bool, addHwVsyncTimestamp, (nsecs_t, std::optional<nsecs_t>, bool*), (override));
- MOCK_METHOD(void, startPeriodTransition, (nsecs_t, bool), (override));
+ MOCK_METHOD(void, onDisplayModeChanged, (ftl::NonNull<DisplayModePtr>, bool), (override));
MOCK_METHOD(void, setIgnorePresentFences, (bool), (override));
MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h
new file mode 100644
index 0000000..b48529f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h
@@ -0,0 +1,32 @@
+/*
+ * 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, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override));
+};
+
+struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback {
+ void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override{};
+};
+} // namespace android::scheduler::mock
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 5403baf..2002bdf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -59,12 +59,6 @@
"-Wunreachable-code",
],
- // FIXME: Workaround LTO build breakage
- // http://b/241699694
- lto: {
- never: true,
- },
-
local_include_dirs: ["include"],
export_include_dirs: ["include"],
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
index 7e382a3..63a2bd0 100644
--- a/services/vibratorservice/TEST_MAPPING
+++ b/services/vibratorservice/TEST_MAPPING
@@ -6,10 +6,6 @@
// TODO(b/293603710): Fix flakiness
{
"exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleRunsOnlyAfterDelay"
- },
- // TODO(b/293623689): Fix flakiness
- {
- "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleMultipleCallbacksRunsInDelayOrder"
}
]
}
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 7c8e695..159b2d5 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -63,7 +63,8 @@
/*
* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
*
- * This version of the extension deprecates the last of grallocusage
+ * This version of the extension deprecates the last of grallocusage and
+ * extends VkNativeBufferANDROID to support passing AHardwareBuffer*
*/
#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
@@ -111,6 +112,9 @@
* usage: gralloc usage requested when the buffer was allocated
* usage2: gralloc usage requested when the buffer was allocated
* usage3: gralloc usage requested when the buffer was allocated
+ * ahb: The AHardwareBuffer* from the actual ANativeWindowBuffer. Caller
+ * maintains ownership of resource. AHardwareBuffer pointer is only valid
+ * for the duration of the function call
*/
typedef struct {
VkStructureType sType;
@@ -121,6 +125,7 @@
int usage; /* DEPRECATED in SPEC_VERSION 6 */
VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */
uint64_t usage3; /* ADDED in SPEC_VERSION 9 */
+ struct AHardwareBuffer* ahb; /* ADDED in SPEC_VERSION 11 */
} VkNativeBufferANDROID;
/*
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index a87f82f..436e6c6 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -109,6 +109,7 @@
"libnativeloader_lazy",
"libnativewindow",
"libvndksupport",
+ "libdl_android",
"android.hardware.graphics.common@1.0",
"libSurfaceFlingerProp",
],
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 5d7a4aa..0e45d2d 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -46,6 +46,8 @@
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+extern "C" android_namespace_t* android_get_exported_namespace(const char*);
+
// #define ENABLE_ALLOC_CALLSTACKS 1
#if ENABLE_ALLOC_CALLSTACKS
#include <utils/CallStack.h>
@@ -159,6 +161,7 @@
"ro.board.platform",
}};
constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW;
+constexpr char RO_VULKAN_APEX_PROPERTY[] = "ro.vulkan.apex";
// LoadDriver returns:
// * 0 when succeed, or
@@ -166,6 +169,7 @@
// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
// HWVULKAN_HARDWARE_MODULE_ID in the library.
int LoadDriver(android_namespace_t* library_namespace,
+ const char* ns_name,
const hwvulkan_module_t** module) {
ATRACE_CALL();
@@ -183,8 +187,10 @@
.library_namespace = library_namespace,
};
so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
- ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
- lib_name.c_str(), dlerror());
+ if (!so) {
+ ALOGE("Could not load %s from %s namespace: %s.",
+ lib_name.c_str(), ns_name, dlerror());
+ }
} else {
// load built-in driver
so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);
@@ -211,12 +217,30 @@
return 0;
}
+int LoadDriverFromApex(const hwvulkan_module_t** module) {
+ ATRACE_CALL();
+
+ auto apex_name = android::base::GetProperty(RO_VULKAN_APEX_PROPERTY, "");
+ if (apex_name == "") {
+ return -ENOENT;
+ }
+ // Get linker namespace for Vulkan APEX
+ std::replace(apex_name.begin(), apex_name.end(), '.', '_');
+ auto ns = android_get_exported_namespace(apex_name.c_str());
+ if (!ns) {
+ return -ENOENT;
+ }
+ android::GraphicsEnv::getInstance().setDriverToLoad(
+ android::GpuStatsInfo::Driver::VULKAN);
+ return LoadDriver(ns, apex_name.c_str(), module);
+}
+
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
ATRACE_CALL();
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN);
- return LoadDriver(nullptr, module);
+ return LoadDriver(nullptr, nullptr, module);
}
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
@@ -227,7 +251,7 @@
return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN_UPDATED);
- int result = LoadDriver(ns, module);
+ int result = LoadDriver(ns, "updatable gfx driver", module);
if (result != 0) {
LOG_ALWAYS_FATAL(
"couldn't find an updated Vulkan implementation from %s",
@@ -256,6 +280,9 @@
result = LoadUpdatedDriver(&module);
if (result == -ENOENT) {
+ result = LoadDriverFromApex(&module);
+ }
+ if (result == -ENOENT) {
result = LoadBuiltinDriver(&module);
}
if (result != 0) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0c48611..6b3c379 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1485,10 +1485,33 @@
return VK_SUCCESS;
}
+ // Look through the create_info pNext chain passed to createSwapchainKHR
+ // for an image compression control struct.
+ // if one is found AND the appropriate extensions are enabled, create a
+ // VkImageCompressionControlEXT structure to pass on to GetPhysicalDeviceImageFormatProperties2
+ void* compression_control_pNext = nullptr;
+ VkImageCompressionControlEXT image_compression = {};
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ compression_control_pNext = &image_compression;
+ } break;
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+
// call GetPhysicalDeviceImageFormatProperties2KHR
VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
- .pNext = nullptr,
+ .pNext = compression_control_pNext,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
@@ -1739,6 +1762,8 @@
}
int query_value;
+ // TODO: Now that we are calling into GPDSC2 directly, this query may be redundant
+ // the call to std::max(min_buffer_count, num_images) may be redundant as well
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
&query_value);
if (err != android::OK || query_value < 0) {
@@ -1755,12 +1780,33 @@
// with extra images (which they can't actually use!).
const uint32_t min_buffer_count = min_undequeued_buffers + 1;
- uint32_t num_images;
- if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
- num_images = std::max(3u, create_info->minImageCount);
- } else {
- num_images = create_info->minImageCount;
- }
+ // Call into GPDSC2 to get the minimum and maximum allowable buffer count for the surface of
+ // interest. This step is only necessary if the app requests a number of images
+ // (create_info->minImageCount) that is less or more than the surface capabilities.
+ // An app should be calling GPDSC2 and using those values to set create_info, but in the
+ // event that the app has hard-coded image counts an error can occur
+ VkSurfacePresentModeEXT present_mode = {
+ VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
+ nullptr,
+ create_info->presentMode
+ };
+ VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
+ &present_mode,
+ create_info->surface
+ };
+ VkSurfaceCapabilities2KHR surface_capabilities2 = {
+ VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
+ nullptr,
+ {},
+ };
+ result = GetPhysicalDeviceSurfaceCapabilities2KHR(GetData(device).driver_physical_device,
+ &surface_info2, &surface_capabilities2);
+
+ uint32_t num_images = create_info->minImageCount;
+ num_images = std::clamp(num_images,
+ surface_capabilities2.surfaceCapabilities.minImageCount,
+ surface_capabilities2.surfaceCapabilities.maxImageCount);
const uint32_t buffer_count = std::max(min_buffer_count, num_images);
err = native_window_set_buffer_count(window, buffer_count);
@@ -1947,6 +1993,8 @@
&image_native_buffer.usage2.producer,
&image_native_buffer.usage2.consumer);
image_native_buffer.usage3 = img.buffer->usage;
+ image_native_buffer.ahb =
+ ANativeWindowBuffer_getHardwareBuffer(img.buffer.get());
image_create.pNext = &image_native_buffer;
ATRACE_BEGIN("CreateImage");
@@ -2123,7 +2171,12 @@
.stride = buffer->stride,
.format = buffer->format,
.usage = int(buffer->usage),
+ .usage3 = buffer->usage,
+ .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer),
};
+ android_convertGralloc0To1Usage(int(buffer->usage),
+ &nb.usage2.producer,
+ &nb.usage2.consumer);
VkBindImageMemoryInfo bimi = {
.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO,
.pNext = &nb,
@@ -2669,7 +2722,12 @@
.stride = buffer->stride,
.format = buffer->format,
.usage = int(buffer->usage),
+ .usage3 = buffer->usage,
+ .ahb = ANativeWindowBuffer_getHardwareBuffer(buffer),
};
+ android_convertGralloc0To1Usage(int(buffer->usage),
+ &native_buffer.usage2.producer,
+ &native_buffer.usage2.consumer);
// Reserve enough space to avoid letting re-allocation invalidate the
// addresses of the elements inside.
out_native_buffers->reserve(bind_info_count);
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
index a6d540b..5112e14 100644
--- a/vulkan/nulldrv/Android.bp
+++ b/vulkan/nulldrv/Android.bp
@@ -46,5 +46,8 @@
"hwvulkan_headers",
"vulkan_headers",
],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "liblog",
+ "libnativewindow",
+ ],
}
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 2e87f17..973e71c 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/hardware_buffer.h>
#include <hardware/hwvulkan.h>
#include <errno.h>
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 935535f..d34851e 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -16,6 +16,8 @@
// WARNING: This file is generated. See ../README.md for instructions.
+#include <android/hardware_buffer.h>
+
#include <algorithm>
#include "null_driver_gen.h"