Merge "Record rendering stability histogram."
diff --git a/Android.bp b/Android.bp
index 615a7a8..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
cc_library_headers {
name: "libandroid_sensor_headers",
- vendor: true,
+ vendor_available: true,
+ host_supported: true,
export_include_dirs: ["include_sensor"],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 260ee8d..f54f132 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -56,6 +56,9 @@
},
{
"include-filter": "*RefreshRateOverlayTest.*"
+ },
+ {
+ "exclude-filter": "*ChildLayerTest#ChildrenSurviveParentDestruction"
}
]
},
diff --git a/aidl/gui/android/view/Surface.aidl b/aidl/gui/android/view/Surface.aidl
index 7e89220..bb3faaf 100644
--- a/aidl/gui/android/view/Surface.aidl
+++ b/aidl/gui/android/view/Surface.aidl
@@ -17,4 +17,4 @@
package android.view;
-parcelable Surface cpp_header "gui/view/Surface.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable Surface cpp_header "gui/view/Surface.h" ndk_header "android/native_window_aidl.h";
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6fb9a4d..48d48ac 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -193,8 +193,9 @@
{ OPT, "events/ext4/ext4_da_write_end/enable" },
{ OPT, "events/ext4/ext4_sync_file_enter/enable" },
{ OPT, "events/ext4/ext4_sync_file_exit/enable" },
- { REQ, "events/block/block_rq_issue/enable" },
- { REQ, "events/block/block_rq_complete/enable" },
+ { OPT, "events/block/block_bio_queue/enable" },
+ { OPT, "events/block/block_bio_complete/enable" },
+ { OPT, "events/ufs/ufshcd_command/enable" },
} },
{ "mmc", "eMMC commands", 0, {
{ REQ, "events/mmc/enable" },
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a60972b..860a2d8 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -126,6 +126,7 @@
],
required: [
"atrace",
+ "bugreport_procdump",
"dmabuf_dump",
"ip",
"iptables",
@@ -154,7 +155,10 @@
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
- static_libs: ["libgmock"],
+ static_libs: [
+ "libc++fs",
+ "libgmock",
+ ],
test_config: "dumpstate_test.xml",
data: [
":dumpstate_test_fixture",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index e42ee05..42e9e0f 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -51,7 +51,7 @@
// Creates a bugreport and exits, thus preserving the oneshot nature of the service.
// Note: takes ownership of data.
-[[noreturn]] static void* dumpstate_thread_main(void* data) {
+[[noreturn]] static void* dumpstate_thread_bugreport(void* data) {
std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
MYLOGD("Finished taking a bugreport. Exiting.\n");
@@ -84,11 +84,28 @@
return android::OK;
}
+binder::Status DumpstateService::preDumpUiData(const std::string&) {
+ std::lock_guard<std::mutex> lock(lock_);
+ MYLOGI("preDumpUiData()");
+
+ if (ds_ != nullptr) {
+ MYLOGE("Error! DumpstateService is currently already being used. Returning.");
+ return exception(binder::Status::EX_SERVICE_SPECIFIC,
+ "DumpstateService is already being used");
+ }
+
+ ds_ = &(Dumpstate::GetInstance());
+ ds_->PreDumpUiData();
+
+ return binder::Status::ok();
+}
+
binder::Status DumpstateService::startBugreport(int32_t calling_uid,
const std::string& calling_package,
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd,
int bugreport_mode,
+ int bugreport_flags,
const sp<IDumpstateListener>& listener,
bool is_screenshot_requested) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
@@ -96,12 +113,12 @@
// Ensure there is only one bugreport in progress at a time.
std::lock_guard<std::mutex> lock(lock_);
if (ds_ != nullptr) {
- MYLOGE("Error! There is already a bugreport in progress. Returning.");
+ MYLOGE("Error! DumpstateService is currently already being used. Returning.");
if (listener != nullptr) {
listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
}
return exception(binder::Status::EX_SERVICE_SPECIFIC,
- "There is already a bugreport in progress");
+ "DumpstateService is already being used");
}
// From here on, all conditions that indicate we are done with this incoming request should
@@ -123,8 +140,8 @@
}
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
- options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
- screenshot_fd, is_screenshot_requested);
+ options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_flags,
+ bugreport_fd, screenshot_fd, is_screenshot_requested);
if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
MYLOGE("Invalid filedescriptor");
@@ -148,10 +165,9 @@
pthread_t thread;
// Initialize dumpstate
ds_->Initialize();
- status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
+ status_t err = pthread_create(&thread, nullptr, dumpstate_thread_bugreport, ds_info);
if (err != 0) {
delete ds_info;
- ds_info = nullptr;
MYLOGE("Could not create a thread");
signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
}
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 3ec8471..997999c 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -38,13 +38,16 @@
status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status preDumpUiData(const std::string& callingPackage) override;
+
binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd, int bugreport_mode,
- const sp<IDumpstateListener>& listener,
+ int bugreport_flags, const sp<IDumpstateListener>& listener,
bool is_screenshot_requested) override;
- binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package);
+ binder::Status cancelBugreport(int32_t calling_uid,
+ const std::string& calling_package) override;
private:
// Dumpstate object which contains all the bugreporting logic.
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 0793f0b..d4323af 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -49,6 +49,23 @@
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
+ // Use pre-dumped data.
+ const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1;
+
+ /**
+ * Speculatively pre-dumps UI data for a bugreport request that might come later.
+ *
+ * <p>Triggers the dump of certain critical UI data, e.g. traces stored in short
+ * ring buffers that might get lost by the time the actual bugreport is requested.
+ *
+ * <p>{@code startBugreport} will then pick the pre-dumped data if:
+ * - {@link BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA} is specified.
+ * - {@code preDumpUiData} and {@code startBugreport} were called by the same UID.
+ *
+ * @param callingPackage package of the original application that requested the report.
+ */
+ void preDumpUiData(@utf8InCpp String callingPackage);
+
/**
* Starts a bugreport in the background.
*
@@ -63,13 +80,14 @@
* @param bugreportFd the file to which the zipped bugreport should be written
* @param screenshotFd the file to which screenshot should be written
* @param bugreportMode the mode that specifies other run time options; must be one of above
+ * @param bugreportFlags flags to customize the bugreport generation
* @param listener callback for updates; optional
* @param isScreenshotRequested indicates screenshot is requested or not
*/
void startBugreport(int callingUid, @utf8InCpp String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener,
- boolean isScreenshotRequested);
+ int bugreportMode, int bugreportFlags,
+ IDumpstateListener listener, boolean isScreenshotRequested);
/**
* Cancels the bugreport currently in progress.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index fe91341..69a1df2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -170,6 +170,7 @@
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log"
+#define UPDATE_ENGINE_PREF_DIR "/data/misc/update_engine/prefs"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
#define PREREBOOT_DATA_DIR "/data/misc/prereboot"
#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
@@ -193,6 +194,8 @@
static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
static const std::string ANR_DIR = "/data/anr/";
static const std::string ANR_FILE_PREFIX = "anr_";
+static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/";
+static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-";
// TODO: temporary variables and functions used during C++ refactoring
@@ -238,6 +241,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";
namespace android {
namespace os {
@@ -1052,7 +1056,7 @@
return;
}
RunCommandToFd(fd, "", {"dumpsys", "netstats", "--proto"},
- CommandOptions::WithTimeout(120).Build());
+ CommandOptions::WithTimeout(5).Build());
bool empty = 0 == lseek(fd, 0, SEEK_END);
if (!empty) {
ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "netstats" + kProtoExt,
@@ -1108,6 +1112,16 @@
RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
}
+static void DumpShutdownCheckpoints() {
+ const bool shutdown_checkpoints_dumped = AddDumps(
+ ds.shutdown_checkpoints_.begin(), ds.shutdown_checkpoints_.end(),
+ "SHUTDOWN CHECKPOINTS", false /* add_to_zip */);
+ if (!shutdown_checkpoints_dumped) {
+ printf("*** NO SHUTDOWN CHECKPOINTS to dump in %s\n\n",
+ SHUTDOWN_CHECKPOINTS_DIR.c_str());
+ }
+}
+
static void DumpDynamicPartitionInfo() {
if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
return;
@@ -1593,16 +1607,16 @@
// via the consent they are shown. Ignores other errors that occur while running various
// commands. The consent checking is currently done around long running tasks, which happen to
// be distributed fairly evenly throughout the function.
-static Dumpstate::RunStatus dumpstate() {
+Dumpstate::RunStatus Dumpstate::dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
// 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;
+ dump_netstats_report, post_process_ui_traces;
if (ds.dump_pool_) {
// Pool was shutdown in DumpstateDefaultAfterCritical method in order to
- // drop root user. Restarts it with two threads for the parallel run.
- ds.dump_pool_->start(/* thread_counts = */2);
+ // drop root user. Restarts it.
+ ds.dump_pool_->start(/* thread_counts = */3);
dump_hals = ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
dump_incident_report = ds.dump_pool_->enqueueTask(
@@ -1612,6 +1626,8 @@
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
@@ -1624,7 +1640,8 @@
RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
"pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20);
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "BUGREPORT_PROCDUMP", {"bugreport_procdump"},
+ CommandOptions::AS_ROOT);
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpVisibleWindowViews);
@@ -1641,9 +1658,6 @@
RunCommand("PROCESSES AND THREADS",
{"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"});
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
- CommandOptions::AS_ROOT);
-
if (ds.dump_pool_) {
WAIT_TASK_WITH_CONSENT_CHECK(std::move(dump_hals));
} else {
@@ -1673,8 +1687,6 @@
RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES");
-
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
@@ -1704,6 +1716,8 @@
DoKmsg();
+ DumpShutdownCheckpoints();
+
DumpIpAddrAndRules();
dump_route_tables();
@@ -1736,11 +1750,6 @@
DumpFile("BINDER STATS", binder_logs_dir + "/stats");
DumpFile("BINDER STATE", binder_logs_dir + "/state");
- /* Add window and surface trace files. */
- if (!PropertiesHelper::IsUserBuild()) {
- ds.AddDir(WMTRACE_DATA_DIR, false);
- }
-
ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
if (ds.dump_pool_) {
@@ -1820,6 +1829,14 @@
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;
}
@@ -1858,11 +1875,14 @@
if (!PropertiesHelper::IsDryRun()) {
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX);
ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX);
+ ds.shutdown_checkpoints_ = GetDumpFds(
+ SHUTDOWN_CHECKPOINTS_DIR, SHUTDOWN_CHECKPOINTS_FILE_PREFIX);
}
ds.AddDir(RECOVERY_DIR, true);
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(UPDATE_ENGINE_LOG_DIR, true);
+ ds.AddDir(UPDATE_ENGINE_PREF_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
@@ -2791,9 +2811,11 @@
}
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
+ int bugreport_flags,
const android::base::unique_fd& bugreport_fd_in,
const android::base::unique_fd& screenshot_fd_in,
bool is_screenshot_requested) {
+ this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
// Duplicate the fds because the passed in fds don't outlive the binder transaction.
bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -2909,6 +2931,7 @@
}
tombstone_data_.clear();
anr_data_.clear();
+ shutdown_checkpoints_.clear();
// Instead of shutdown the pool, we delete temporary files directly since
// shutdown blocking the call.
@@ -2920,6 +2943,10 @@
}
}
+void Dumpstate::PreDumpUiData() {
+ MaybeSnapshotUiTraces();
+}
+
/*
* Dumps relevant information to a bugreport based on the given options.
*
@@ -3111,9 +3138,9 @@
// The trace file is added to the zip by MaybeAddSystemTraceToZip().
MaybeSnapshotSystemTrace();
- // If a winscope trace is running, snapshot it now. It will be pulled into bugreport later
- // from WMTRACE_DATA_DIR.
- MaybeSnapshotWinTrace();
+ // Snapshot the UI traces now (if running).
+ // The trace files will be added to bugreport later.
+ MaybeSnapshotUiTraces();
}
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
@@ -3192,6 +3219,7 @@
tombstone_data_.clear();
anr_data_.clear();
+ shutdown_checkpoints_.clear();
return (consent_callback_ != nullptr &&
consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
@@ -3227,15 +3255,53 @@
// file in the later stages.
}
-void Dumpstate::MaybeSnapshotWinTrace() {
+void Dumpstate::MaybeSnapshotUiTraces() {
+ if (PropertiesHelper::IsUserBuild() || options_->use_predumped_ui_data) {
+ return;
+ }
+
// Currently WindowManagerService and InputMethodManagerSerivice support WinScope protocol.
- for (const auto& service : {"window", "input_method"}) {
+ for (const auto& service : {"input_method", "window"}) {
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.
"", {"cmd", service, "tracing", "save-for-bugreport"},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
}
+
+ 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() {
+ if (PropertiesHelper::IsUserBuild()) {
+ return;
+ }
+
+ ds.AddDir(WMTRACE_DATA_DIR, false);
}
void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) {
@@ -3922,15 +3988,6 @@
return;
}
-void do_showmap(int pid, const char *name) {
- char title[255];
- char arg[255];
-
- snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name);
- snprintf(arg, sizeof(arg), "%d", pid);
- RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT);
-}
-
int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index ee6b1ae..9f894b5 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -204,6 +204,12 @@
BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT
};
+ // The flags used to customize bugreport requests.
+ enum BugreportFlag {
+ BUGREPORT_USE_PREDUMPED_UI_DATA =
+ android::os::IDumpstate::BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
+ };
+
static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS;
static Dumpstate& GetInstance();
@@ -333,6 +339,12 @@
struct DumpOptions;
+ /**
+ * Pre-dump critical UI data, e.g. data stored in short ring buffers that might get lost
+ * by the time the actual bugreport is requested.
+ */
+ void PreDumpUiData();
+
/*
* Main entry point for running a complete bugreport.
*
@@ -396,6 +408,8 @@
// TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
// The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT;
+ // Will use data collected through a previous call to PreDumpUiData().
+ bool use_predumped_ui_data;
// File descriptor to output zip file. Takes precedence over out_dir.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
@@ -414,7 +428,8 @@
RunStatus Initialize(int argc, char* argv[]);
/* Initializes options from the requested mode. */
- void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
+ void Initialize(BugreportMode bugreport_mode, int bugreport_flags,
+ const android::base::unique_fd& bugreport_fd,
const android::base::unique_fd& screenshot_fd,
bool is_screenshot_requested);
@@ -499,6 +514,9 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // List of open shutdown checkpoint files.
+ std::vector<DumpData> shutdown_checkpoints_;
+
// A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
@@ -532,10 +550,13 @@
RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
RunStatus DumpstateDefaultAfterCritical();
+ RunStatus dumpstate();
void MaybeTakeEarlyScreenshot();
void MaybeSnapshotSystemTrace();
- void MaybeSnapshotWinTrace();
+ void MaybeSnapshotUiTraces();
+ void MaybePostProcessUiTraces();
+ void MaybeAddUiTracesToZip();
void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid);
@@ -619,9 +640,6 @@
/* Displays a processes times */
void show_showtime(int pid, const char *name);
-/* Runs "showmap" for a process */
-void do_showmap(int pid, const char *name);
-
/* Gets the dmesg output for the kernel */
void do_dmesg();
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
index ec89c0d..a634f93 100644
--- a/cmds/dumpstate/main.cpp
+++ b/cmds/dumpstate/main.cpp
@@ -56,7 +56,7 @@
MYLOGE("Unable to start 'dumpstate' service: %d", ret);
exit(1);
}
- MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()");
+ MYLOGI("'dumpstate' service started and will wait for a call");
// Waits forever for an incoming connection.
// TODO(b/111441001): should this time out?
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 28e5ee2..b091c8e 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -497,14 +497,17 @@
// Prepare arguments
unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
+ int flags = 0;
EXPECT_NE(bugreport_fd.get(), -1);
EXPECT_NE(screenshot_fd.get(), -1);
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
+ ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+ std::move(screenshot_fd),
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener,
+ true);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
EXPECT_TRUE(status.isOk());
@@ -532,6 +535,7 @@
// Prepare arguments
unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+ int flags = 0;
EXPECT_NE(bugreport_fd.get(), -1);
EXPECT_NE(screenshot_fd.get(), -1);
@@ -539,9 +543,9 @@
// Call startBugreport with bad arguments.
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- 2000, // invalid bugreport mode
- listener, false);
+ ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+ std::move(screenshot_fd), 2000, // invalid bugreport mode
+ flags, listener, false);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
// The service should have died, freeing itself up for a new invocation.
@@ -563,6 +567,7 @@
unique_fd bugreport_fd2(dup(bugreport_fd.get()));
unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
unique_fd screenshot_fd2(dup(screenshot_fd.get()));
+ int flags = 0;
EXPECT_NE(bugreport_fd.get(), -1);
EXPECT_NE(bugreport_fd2.get(), -1);
@@ -571,14 +576,18 @@
sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
+ ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
+ std::move(screenshot_fd),
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1,
+ true);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
- status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
+ status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2),
+ std::move(screenshot_fd2),
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags,
+ listener2, true);
EXPECT_FALSE(status.isOk());
WaitTillExecutionComplete(listener2.get());
EXPECT_EQ(listener2->getErrorCode(),
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 70b4e5c..1ffcafa 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -31,6 +31,7 @@
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
+#include <filesystem>
#include <thread>
#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
@@ -237,7 +238,7 @@
}
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true);
EXPECT_TRUE(options_.do_screenshot);
// Other options retain default values
@@ -251,7 +252,7 @@
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, 0, fd, fd, true);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.do_screenshot);
@@ -265,7 +266,7 @@
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, 0, fd, fd, false);
EXPECT_TRUE(options_.is_remote_mode);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_FALSE(options_.do_screenshot);
@@ -279,7 +280,7 @@
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, 0, fd, fd, true);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_progress_updates);
@@ -294,7 +295,7 @@
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, 0, fd, fd, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.telephony_only);
EXPECT_TRUE(options_.do_progress_updates);
@@ -309,7 +310,7 @@
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, 0, fd, fd, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.wifi_only);
@@ -982,6 +983,19 @@
EXPECT_FALSE(ds.dump_pool_);
}
+TEST_F(DumpstateBaseTest, PreDumpUiData) {
+ // SurfaceFlinger's transactions trace is always enabled, i.e. it is always pre-dumped
+ static const auto kTransactionsTrace =
+ std::filesystem::path {"/data/misc/wmtrace/transactions_trace.winscope"};
+
+ std::system(("rm " + kTransactionsTrace.string()).c_str());
+ EXPECT_FALSE(std::filesystem::exists(kTransactionsTrace));
+
+ Dumpstate& ds_ = Dumpstate::GetInstance();
+ ds_.PreDumpUiData();
+ EXPECT_TRUE(std::filesystem::exists(kTransactionsTrace));
+}
+
class ZippedBugReportStreamTest : public DumpstateBaseTest {
public:
void SetUp() {
@@ -1045,11 +1059,6 @@
VerifyEntry(handle_, bugreport_txt_name, &entry);
}
-class DumpstateServiceTest : public DumpstateBaseTest {
- public:
- DumpstateService dss;
-};
-
class ProgressTest : public DumpstateBaseTest {
public:
Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index f0c19b9..b8e5ce1 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -60,6 +60,7 @@
MOCK_METHOD1(isDeclared, bool(const String16&));
MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&));
+ MOCK_METHOD1(getUpdatableNames, Vector<String16>(const String16&));
MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&));
MOCK_METHOD2(registerForNotifications, status_t(const String16&,
const sp<LocalRegistrationCallback>&));
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index 01f7d30..c163095 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -35,9 +35,12 @@
GLHelper::~GLHelper() {
}
-bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) {
+bool GLHelper::setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs,
+ size_t numShaders) {
bool result;
+ mDisplayToken = displayToken;
+
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (mDisplay == EGL_NO_DISPLAY) {
fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError());
@@ -221,14 +224,8 @@
}
bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
- const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken();
- if (dpy == nullptr) {
- fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n");
- return false;
- }
-
ui::DisplayMode mode;
- status_t err = mSurfaceComposerClient->getActiveDisplayMode(dpy, &mode);
+ status_t err = mSurfaceComposerClient->getActiveDisplayMode(mDisplayToken, &mode);
if (err != NO_ERROR) {
fprintf(stderr, "SurfaceComposer::getActiveDisplayMode failed: %#x\n", err);
return false;
diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h
index d09463a..5194f50 100644
--- a/cmds/flatland/GLHelper.h
+++ b/cmds/flatland/GLHelper.h
@@ -44,7 +44,7 @@
~GLHelper();
- bool setUp(const ShaderDesc* shaderDescs, size_t numShaders);
+ bool setUp(const sp<IBinder>& displayToken, const ShaderDesc* shaderDescs, size_t numShaders);
void tearDown();
@@ -87,6 +87,8 @@
size_t mNumShaders;
GLuint mDitherTexture;
+
+ sp<IBinder> mDisplayToken;
};
} // namespace android
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index 7ceb397..6d14d56 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -20,6 +20,7 @@
#include <gui/SurfaceControl.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <ui/Fence.h>
#include <utils/Trace.h>
@@ -34,9 +35,10 @@
using namespace ::android;
-static uint32_t g_SleepBetweenSamplesMs = 0;
-static bool g_PresentToWindow = false;
-static size_t g_BenchmarkNameLen = 0;
+static uint32_t g_SleepBetweenSamplesMs = 0;
+static bool g_PresentToWindow = false;
+static size_t g_BenchmarkNameLen = 0;
+static sp<IBinder> g_DisplayToken = nullptr;
struct BenchmarkDesc {
// The name of the test.
@@ -393,7 +395,7 @@
uint32_t h = mDesc.runHeights[mInstance];
mGLHelper = new GLHelper();
- result = mGLHelper->setUp(shaders, NELEMS(shaders));
+ result = mGLHelper->setUp(g_DisplayToken, shaders, NELEMS(shaders));
if (!result) {
return false;
}
@@ -718,13 +720,17 @@
}
// Print the command usage help to stderr.
-static void showHelp(const char *cmd) {
- fprintf(stderr, "usage: %s [options]\n", cmd);
- fprintf(stderr, "options include:\n"
- " -s N sleep for N ms between samples\n"
- " -d display the test frame to a window\n"
- " --help print this helpful message and exit\n"
- );
+static void showHelp(const char* cmd) {
+ fprintf(stderr, "usage: %s [options]\n", cmd);
+ fprintf(
+ stderr,
+ "options include:\n"
+ " -s N sleep for N ms between samples\n"
+ " -d display the test frame to a window\n"
+ " -i display-id specify a display ID to use for multi-display device\n"
+ " see \"dumpsys SurfaceFlinger --display-id\" for valid "
+ "display IDs\n"
+ " --help print this helpful message and exit\n");
}
int main(int argc, char** argv) {
@@ -733,6 +739,14 @@
exit(0);
}
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ fprintf(stderr, "Failed to get ID for any displays.\n");
+ exit(3);
+ }
+
+ std::optional<PhysicalDisplayId> displayId;
+
for (;;) {
int ret;
int option_index = 0;
@@ -741,7 +755,7 @@
{ 0, 0, 0, 0 }
};
- ret = getopt_long(argc, argv, "ds:",
+ ret = getopt_long(argc, argv, "ds:i:",
long_options, &option_index);
if (ret < 0) {
@@ -757,6 +771,14 @@
g_SleepBetweenSamplesMs = atoi(optarg);
break;
+ case 'i':
+ displayId = DisplayId::fromValue<PhysicalDisplayId>(atoll(optarg));
+ if (!displayId) {
+ fprintf(stderr, "Invalid display ID: %s.\n", optarg);
+ exit(4);
+ }
+ break;
+
case 0:
if (strcmp(long_options[option_index].name, "help")) {
showHelp(argv[0]);
@@ -770,6 +792,22 @@
}
}
+ if (!displayId) { // no display id is specified
+ if (ids.size() == 1) {
+ displayId = ids.front();
+ } else {
+ fprintf(stderr, "Please specify a display ID for multi-display device.\n");
+ showHelp(argv[0]);
+ exit(5);
+ }
+ }
+
+ g_DisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+ if (g_DisplayToken == nullptr) {
+ fprintf(stderr, "SurfaceComposer::getPhysicalDisplayToken failed.\n");
+ exit(6);
+ }
+
g_BenchmarkNameLen = maxBenchmarkNameLen();
printf(" cmdline:");
@@ -782,4 +820,6 @@
fprintf(stderr, "exiting due to error.\n");
return 1;
}
+
+ return 0;
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b1283eb..bb6639e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -501,10 +501,6 @@
}
static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
- if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
- return true;
- }
-
int32_t uid = multiuser_get_uid(userId, appId);
int shared_app_gid = multiuser_get_shared_gid(userId, appId);
if (shared_app_gid == -1) {
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index ebb7891..34ea759 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1956,7 +1956,7 @@
join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
debuggable, boot_complete, for_restore, target_sdk_version,
enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
- compilation_reason);
+ background_job_compile, compilation_reason);
bool cancelled = false;
pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index e978e79..6a3120c 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -27,6 +27,7 @@
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
+#include <sys/wait.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -504,6 +505,11 @@
return 0;
}
+ if (WIFSIGNALED(dexopt_result)) {
+ LOG(WARNING) << "Interrupted by signal " << WTERMSIG(dexopt_result) ;
+ return dexopt_result;
+ }
+
// If this was a profile-guided run, we may have profile version issues. Try to downgrade,
// if possible.
if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/cmds/installd/otapreopt.rc b/cmds/installd/otapreopt.rc
index 059ae75..0bad0c5 100644
--- a/cmds/installd/otapreopt.rc
+++ b/cmds/installd/otapreopt.rc
@@ -5,4 +5,4 @@
# The dalvik-cache was not moved itself, so as to restrict the rights of otapreopt_slot.
# But now the relabeling is annoying as there is no force option available here. So
# explicitly list all the ISAs we know.
- restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/mips /data/dalvik-cache/mips64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64
+ restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/riscv64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 51c4589..4221a3a 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -81,6 +81,7 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
bool use_jitzygote,
+ bool background_job_compile,
const char* compilation_reason) {
PrepareBootImageFlags(use_jitzygote);
@@ -92,7 +93,8 @@
debuggable, target_sdk_version, enable_hidden_api_checks,
generate_compact_dex, compilation_reason);
- PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore);
+ PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore,
+ background_job_compile);
const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
@@ -296,7 +298,8 @@
}
void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
- bool for_restore) {
+ bool for_restore,
+ bool background_job_compile) {
// CPU set
{
std::string cpu_set_format = "--cpu-set=%s";
@@ -306,7 +309,12 @@
"dalvik.vm.restore-dex2oat-cpu-set",
"dalvik.vm.dex2oat-cpu-set",
cpu_set_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format))
+ : (background_job_compile
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.background-dex2oat-cpu-set",
+ "dalvik.vm.dex2oat-cpu-set",
+ cpu_set_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)))
: MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format);
AddArg(dex2oat_cpu_set_arg);
}
@@ -320,7 +328,12 @@
"dalvik.vm.restore-dex2oat-threads",
"dalvik.vm.dex2oat-threads",
threads_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
+ : (background_job_compile
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.background-dex2oat-threads",
+ "dalvik.vm.dex2oat-threads",
+ threads_format)
+ : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)))
: MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
AddArg(dex2oat_threads_arg);
}
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 559244f..c13e1f1 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -51,6 +51,7 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
bool use_jitzygote,
+ bool background_job_compile,
const char* compilation_reason);
void Exec(int exit_code);
@@ -76,7 +77,9 @@
bool enable_hidden_api_checks,
bool generate_compact_dex,
const char* compilation_reason);
- void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore);
+ void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
+ bool for_restore,
+ bool background_job_compile);
virtual std::string GetProperty(const std::string& key, const std::string& default_value);
virtual bool GetBoolProperty(const std::string& key, bool default_value);
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 2a8135a..304ba7b 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -115,6 +115,7 @@
bool enable_hidden_api_checks = false;
bool generate_compact_dex = true;
bool use_jitzygote = false;
+ bool background_job_compile = false;
const char* compilation_reason = nullptr;
};
@@ -259,6 +260,7 @@
args->enable_hidden_api_checks,
args->generate_compact_dex,
args->use_jitzygote,
+ args->background_job_compile,
args->compilation_reason);
runner.Exec(/*exit_code=*/ 0);
}
@@ -375,6 +377,30 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground) {
+ setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "1,3");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,3");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, CpuSetPostBootCompleteBackground_Backup) {
+ setSystemProperty("dalvik.vm.background-dex2oat-cpu-set", "");
+ setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("--cpu-set", "=1,2");
+ VerifyExpectedFlags();
+}
+
TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) {
setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2");
setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3");
@@ -481,6 +507,30 @@
VerifyExpectedFlags();
}
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground) {
+ setSystemProperty("dalvik.vm.background-dex2oat-threads", "2");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "2");
+ VerifyExpectedFlags();
+}
+
+TEST_F(RunDex2OatTest, ThreadsPostBootCompleteBackground_Backup) {
+ setSystemProperty("dalvik.vm.background-dex2oat-threads", "");
+ setSystemProperty("dalvik.vm.dex2oat-threads", "3");
+ auto args = RunDex2OatArgs::MakeDefaultTestArgs();
+ args->post_bootcomplete = true;
+ args->background_job_compile = true;
+ CallRunDex2Oat(std::move(args));
+
+ SetExpectedFlagUsed("-j", "3");
+ VerifyExpectedFlags();
+}
+
TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) {
setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4");
setSystemProperty("dalvik.vm.dex2oat-threads", "5");
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 6ef41e3..3b589dc 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
@@ -52,22 +53,7 @@
constexpr int kTimeoutMs = 60000;
-// TODO(calin): try to dedup this code.
-#if defined(__arm__)
-static const std::string kRuntimeIsa = "arm";
-#elif defined(__aarch64__)
-static const std::string kRuntimeIsa = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-static const std::string kRuntimeIsa = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-static const std::string kRuntimeIsa = "mips64";
-#elif defined(__i386__)
-static const std::string kRuntimeIsa = "x86";
-#elif defined(__x86_64__)
-static const std::string kRuntimeIsa = "x86_64";
-#else
-static const std::string kRuntimeIsa = "none";
-#endif
+static const std::string kRuntimeIsa = ABI_STRING;
int get_property(const char *key, char *value, const char *default_value) {
return property_get(key, value, default_value);
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index fd879c6..1386660 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -48,14 +48,6 @@
}
cc_binary {
- name: "servicemanager.microdroid",
- defaults: ["servicemanager_defaults"],
- init_rc: ["servicemanager.microdroid.rc"],
- srcs: ["main.cpp"],
- bootstrap: true,
-}
-
-cc_binary {
name: "servicemanager.recovery",
stem: "servicemanager",
recovery: true,
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index a99ccae..cc038ae 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -49,14 +49,14 @@
#ifdef __ANDROID_RECOVERY__
auto vintfObject = vintf::VintfObjectRecovery::GetInstance();
if (vintfObject == nullptr) {
- LOG(ERROR) << "NULL VintfObjectRecovery!";
+ ALOGE("NULL VintfObjectRecovery!");
return {};
}
return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}};
#else
auto vintfObject = vintf::VintfObject::GetInstance();
if (vintfObject == nullptr) {
- LOG(ERROR) << "NULL VintfObject!";
+ ALOGE("NULL VintfObject!");
return {};
}
return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"},
@@ -68,10 +68,10 @@
static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) {
if (mwd.manifest == nullptr) {
- LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
- // note, we explicitly do not retry here, so that we can detect VINTF
- // or other bugs (b/151696835)
- continue;
+ ALOGE("NULL VINTF MANIFEST!: %s", mwd.description);
+ // note, we explicitly do not retry here, so that we can detect VINTF
+ // or other bugs (b/151696835)
+ continue;
}
if (func(mwd)) return true;
}
@@ -87,8 +87,9 @@
size_t firstSlash = name.find('/');
size_t lastDot = name.rfind('.', firstSlash);
if (firstSlash == std::string::npos || lastDot == std::string::npos) {
- LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. "
- << "some.package.foo.IFoo/default) but got: " << name;
+ ALOGE("VINTF HALs require names in the format type/instance (e.g. "
+ "some.package.foo.IFoo/default) but got: %s",
+ name.c_str());
return false;
}
aname->package = name.substr(0, lastDot);
@@ -104,7 +105,7 @@
bool found = forEachManifest([&](const ManifestWithDescription& mwd) {
if (mwd.manifest->hasAidlInstance(aname.package, aname.iface, aname.instance)) {
- LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
+ ALOGI("Found %s in %s VINTF manifest.", name.c_str(), mwd.description);
return true; // break
}
return false; // continue
@@ -113,8 +114,8 @@
if (!found) {
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- LOG(INFO) << "Could not find " << aname.package << "." << aname.iface << "/"
- << aname.instance << " in the VINTF manifest.";
+ ALOGI("Could not find %s.%s/%s in the VINTF manifest.", aname.package.c_str(),
+ aname.iface.c_str(), aname.instance.c_str());
}
return found;
@@ -135,12 +136,33 @@
updatableViaApex = manifestInstance.updatableViaApex();
return false; // break (libvintf uses opposite convention)
});
+ if (updatableViaApex.has_value()) return true; // break (found match)
return false; // continue
});
return updatableViaApex;
}
+static std::vector<std::string> getVintfUpdatableInstances(const std::string& apexName) {
+ std::vector<std::string> instances;
+
+ forEachManifest([&](const ManifestWithDescription& mwd) {
+ mwd.manifest->forEachInstance([&](const auto& manifestInstance) {
+ if (manifestInstance.format() == vintf::HalFormat::AIDL &&
+ manifestInstance.updatableViaApex().has_value() &&
+ manifestInstance.updatableViaApex().value() == apexName) {
+ std::string aname = manifestInstance.package() + "." +
+ manifestInstance.interface() + "/" + manifestInstance.instance();
+ instances.push_back(aname);
+ }
+ return true; // continue (libvintf uses opposite convention)
+ });
+ return false; // continue
+ });
+
+ return instances;
+}
+
static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) {
AidlName aname;
if (!AidlName::fill(name, &aname)) return std::nullopt;
@@ -173,7 +195,9 @@
static std::vector<std::string> getVintfInstances(const std::string& interface) {
size_t lastDot = interface.rfind('.');
if (lastDot == std::string::npos) {
- LOG(ERROR) << "VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) but got: " << interface;
+ ALOGE("VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) "
+ "but got: %s",
+ interface.c_str());
return {};
}
const std::string package = interface.substr(0, lastDot);
@@ -308,7 +332,7 @@
}
if (!isValidServiceName(name)) {
- LOG(ERROR) << "Invalid service name: " << name;
+ ALOGE("Invalid service name: %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name");
}
@@ -322,7 +346,7 @@
// implicitly unlinked when the binder is removed
if (binder->remoteBinder() != nullptr &&
binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
- LOG(ERROR) << "Could not linkToDeath when adding " << name;
+ ALOGE("Could not linkToDeath when adding %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
}
@@ -335,21 +359,20 @@
// only trying to detect when two different services are accidentally installed.
if (existing.ctx.uid != ctx.uid) {
- LOG(WARNING) << "Service '" << name << "' originally registered from UID "
- << existing.ctx.uid << " but it is now being registered from UID "
- << ctx.uid << ". Multiple instances installed?";
+ ALOGW("Service '%s' originally registered from UID %u but it is now being registered "
+ "from UID %u. Multiple instances installed?",
+ name.c_str(), existing.ctx.uid, ctx.uid);
}
if (existing.ctx.sid != ctx.sid) {
- LOG(WARNING) << "Service '" << name << "' originally registered from SID "
- << existing.ctx.sid << " but it is now being registered from SID "
- << ctx.sid << ". Multiple instances installed?";
+ ALOGW("Service '%s' originally registered from SID %s but it is now being registered "
+ "from SID %s. Multiple instances installed?",
+ name.c_str(), existing.ctx.sid.c_str(), ctx.sid.c_str());
}
- LOG(INFO) << "Service '" << name << "' originally registered from PID "
- << existing.ctx.debugPid << " but it is being registered again from PID "
- << ctx.debugPid
- << ". Bad state? Late death notification? Multiple instances installed?";
+ ALOGI("Service '%s' originally registered from PID %d but it is being registered again "
+ "from PID %d. Bad state? Late death notification? Multiple instances installed?",
+ name.c_str(), existing.ctx.debugPid, ctx.debugPid);
}
// Overwrite the old service if it exists
@@ -406,7 +429,7 @@
}
if (!isValidServiceName(name)) {
- LOG(ERROR) << "Invalid service name: " << name;
+ ALOGE("Invalid service name: %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
@@ -417,7 +440,7 @@
if (OK !=
IInterface::asBinder(callback)->linkToDeath(
sp<ServiceManager>::fromExisting(this))) {
- LOG(ERROR) << "Could not linkToDeath when adding " << name;
+ ALOGE("Could not linkToDeath when adding %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -449,7 +472,7 @@
}
if (!found) {
- LOG(ERROR) << "Trying to unregister callback, but none exists " << name;
+ ALOGE("Trying to unregister callback, but none exists %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -510,6 +533,30 @@
return Status::ok();
}
+Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& apexName,
+ std::vector<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ std::vector<std::string> apexUpdatableInstances;
+#ifndef VENDORSERVICEMANAGER
+ apexUpdatableInstances = getVintfUpdatableInstances(apexName);
+#endif
+
+ outReturn->clear();
+
+ for (const std::string& instance : apexUpdatableInstances) {
+ if (mAccess->canFind(ctx, instance)) {
+ outReturn->push_back(instance);
+ }
+ }
+
+ if (outReturn->size() == 0 && apexUpdatableInstances.size() != 0) {
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denial");
+ }
+
+ return Status::ok();
+}
+
Status ServiceManager::getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) {
auto ctx = mAccess->getCallingContext();
@@ -566,15 +613,17 @@
}
void ServiceManager::tryStartService(const std::string& name) {
- ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service",
+ ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service. (if it's not "
+ "configured to be a lazy service, it may be stuck starting or still starting).",
name.c_str());
std::thread([=] {
if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
- LOG(INFO) << "Tried to start aidl service " << name
- << " as a lazy service, but was unable to. Usually this happens when a "
- "service is not installed, but if the service is intended to be used as a "
- "lazy service, then it may be configured incorrectly.";
+ ALOGI("Tried to start aidl service %s as a lazy service, but was unable to. Usually "
+ "this happens when a "
+ "service is not installed, but if the service is intended to be used as a "
+ "lazy service, then it may be configured incorrectly.",
+ name.c_str());
}
}).detach();
}
@@ -592,24 +641,25 @@
auto serviceIt = mNameToService.find(name);
if (serviceIt == mNameToService.end()) {
- LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
+ ALOGE("Could not add callback for nonexistent service: %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
- LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
+ ALOGW("Only a server can register for client callbacks (for %s)", name.c_str());
return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
}
if (serviceIt->second.binder != service) {
- LOG(WARNING) << "Tried to register client callback for " << name
- << " but a different service is registered under this name.";
+ ALOGW("Tried to register client callback for %s but a different service is registered "
+ "under this name.",
+ name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
}
if (OK !=
IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
- LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
+ ALOGE("Could not linkToDeath when adding client callback for %s", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -694,7 +744,7 @@
void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
auto serviceIt = mNameToService.find(serviceName);
if (serviceIt == mNameToService.end()) {
- LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName;
+ ALOGW("sendClientCallbackNotifications could not find service %s", serviceName.c_str());
return;
}
Service& service = serviceIt->second;
@@ -702,7 +752,7 @@
CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
<< " so we can't tell clients again that we have client: " << hasClients;
- LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients;
+ ALOGI("Notifying %s they have clients: %d", serviceName.c_str(), hasClients);
auto ccIt = mNameToClientCallback.find(serviceName);
CHECK(ccIt != mNameToClientCallback.end())
@@ -727,26 +777,26 @@
auto serviceIt = mNameToService.find(name);
if (serviceIt == mNameToService.end()) {
- LOG(WARNING) << "Tried to unregister " << name
- << ", but that service wasn't registered to begin with.";
+ ALOGW("Tried to unregister %s, but that service wasn't registered to begin with.",
+ name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
- LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
+ ALOGW("Only a server can unregister itself (for %s)", name.c_str());
return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
}
sp<IBinder> storedBinder = serviceIt->second.binder;
if (binder != storedBinder) {
- LOG(WARNING) << "Tried to unregister " << name
- << ", but a different service is registered under this name.";
+ ALOGW("Tried to unregister %s, but a different service is registered under this name.",
+ name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
if (serviceIt->second.guaranteeClient) {
- LOG(INFO) << "Tried to unregister " << name << ", but there is about to be a client.";
+ ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
@@ -759,7 +809,7 @@
// So, if clients > 2, then at least one other service on the system must hold a refcount.
if (clients < 0 || clients > 2) {
// client callbacks are either disabled or there are other clients
- LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
+ ALOGI("Tried to unregister %s, but there are clients: %d", name.c_str(), clients);
// Set this flag to ensure the clients are acknowledged in the next callback
serviceIt->second.guaranteeClient = true;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 07b79f8..b24c11c 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -49,6 +49,8 @@
binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status updatableViaApex(const std::string& name,
std::optional<std::string>* outReturn) override;
+ binder::Status getUpdatableNames(const std::string& apexName,
+ std::vector<std::string>* outReturn) override;
binder::Status getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index a831d1b..c1a04dd 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -111,9 +111,7 @@
};
int main(int argc, char** argv) {
-#ifdef __ANDROID_RECOVERY__
android::base::InitLogging(argv, android::base::KernelLogger);
-#endif
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
deleted file mode 100644
index c516043..0000000
--- a/cmds/servicemanager/servicemanager.microdroid.rc
+++ /dev/null
@@ -1,9 +0,0 @@
-service servicemanager /system/bin/servicemanager.microdroid
- class core
- user system
- group system readproc
- critical
- onrestart setprop servicemanager.ready false
- onrestart restart apexd
- task_profiles ServiceCapacityLow
- shutdown critical
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 6b35265..3bd6db5 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -3,6 +3,7 @@
user system
group system readproc
critical
+ file /dev/kmsg w
onrestart setprop servicemanager.ready false
onrestart restart apexd
onrestart restart audioserver
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 5d5a75e..0fd8d8e 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -14,13 +14,15 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <android/os/BnServiceCallback.h>
#include <binder/Binder.h>
-#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <cutils/android_filesystem_config.h>
-#include <gtest/gtest.h>
#include <gmock/gmock.h>
+#include <gtest/gtest.h>
#include "Access.h"
#include "ServiceManager.h"
@@ -75,6 +77,11 @@
return sm;
}
+static bool isCuttlefish() {
+ return android::base::StartsWith(android::base::GetProperty("ro.product.vendor.device", ""),
+ "vsoc_");
+}
+
TEST(AddService, HappyHappy) {
auto sm = getPermissiveServiceManager();
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
@@ -306,6 +313,49 @@
EXPECT_THAT(out, ElementsAre("sa"));
}
+TEST(Vintf, UpdatableViaApex) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::optional<std::string> updatableViaApex;
+ EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider/internal/0",
+ &updatableViaApex)
+ .isOk());
+ EXPECT_EQ(std::make_optional<std::string>("com.google.emulated.camera.provider.hal"),
+ updatableViaApex);
+}
+
+TEST(Vintf, UpdatableViaApex_InvalidNameReturnsNullOpt) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::optional<std::string> updatableViaApex;
+ EXPECT_TRUE(sm->updatableViaApex("android.hardware.camera.provider.ICameraProvider",
+ &updatableViaApex)
+ .isOk()); // missing instance name
+ EXPECT_EQ(std::nullopt, updatableViaApex);
+}
+
+TEST(Vintf, GetUpdatableNames) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::vector<std::string> names;
+ EXPECT_TRUE(sm->getUpdatableNames("com.google.emulated.camera.provider.hal", &names).isOk());
+ EXPECT_EQ(std::vector<
+ std::string>{"android.hardware.camera.provider.ICameraProvider/internal/0"},
+ names);
+}
+
+TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) {
+ if (!isCuttlefish()) GTEST_SKIP() << "Skipping non-Cuttlefish devices";
+
+ auto sm = getPermissiveServiceManager();
+ std::vector<std::string> names;
+ EXPECT_TRUE(sm->getUpdatableNames("non.existing.apex.name", &names).isOk());
+ EXPECT_EQ(std::vector<std::string>{}, names);
+}
+
class CallbackHistorian : public BnServiceCallback {
Status onRegistration(const std::string& name, const sp<IBinder>& binder) override {
registrations.push_back(name);
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
index c9305a1..80af1d1 100644
--- a/cmds/servicemanager/vndservicemanager.rc
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -2,6 +2,7 @@
class core
user system
group system readproc
+ file /dev/kmsg w
task_profiles ServiceCapacityLow
onrestart class_restart main
onrestart class_restart hal
diff --git a/cmds/surfacereplayer/Android.bp b/cmds/surfacereplayer/Android.bp
deleted file mode 100644
index 34fc8b1..0000000
--- a/cmds/surfacereplayer/Android.bp
+++ /dev/null
@@ -1,13 +0,0 @@
-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"],
-}
-
-subdirs = [
- "proto",
- "replayer",
-]
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
deleted file mode 100644
index 32bcc83..0000000
--- a/cmds/surfacereplayer/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
deleted file mode 100644
index 23b54ee..0000000
--- a/cmds/surfacereplayer/proto/Android.bp
+++ /dev/null
@@ -1,23 +0,0 @@
-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_static {
- name: "libtrace_proto",
- srcs: [
- "src/trace.proto",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- proto: {
- type: "lite",
- export_proto_headers: true,
- },
-}
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
deleted file mode 100644
index a177027..0000000
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ /dev/null
@@ -1,225 +0,0 @@
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-package android.surfaceflinger;
-
-message Trace {
- repeated Increment increment = 1;
-}
-
-message Increment {
- required int64 time_stamp = 1;
-
- oneof increment {
- Transaction transaction = 2;
- SurfaceCreation surface_creation = 3;
- SurfaceDeletion surface_deletion = 4;
- BufferUpdate buffer_update = 5;
- VSyncEvent vsync_event = 6;
- DisplayCreation display_creation = 7;
- DisplayDeletion display_deletion = 8;
- PowerModeUpdate power_mode_update = 9;
- }
-}
-
-message Transaction {
- repeated SurfaceChange surface_change = 1;
- repeated DisplayChange display_change = 2;
-
- required bool synchronous = 3;
- required bool animation = 4;
- optional Origin origin = 5;
- optional uint64 id = 6;
-}
-
-message SurfaceChange {
- required int32 id = 1;
- reserved 7;
- oneof SurfaceChange {
- PositionChange position = 2;
- SizeChange size = 3;
- AlphaChange alpha = 4;
- LayerChange layer = 5;
- CropChange crop = 6;
- MatrixChange matrix = 8;
- TransparentRegionHintChange transparent_region_hint = 10;
- LayerStackChange layer_stack = 11;
- HiddenFlagChange hidden_flag = 12;
- OpaqueFlagChange opaque_flag = 13;
- SecureFlagChange secure_flag = 14;
- CornerRadiusChange corner_radius = 16;
- ReparentChange reparent = 17;
- RelativeParentChange relative_parent = 18;
- BackgroundBlurRadiusChange background_blur_radius = 20;
- ShadowRadiusChange shadow_radius = 21;
- BlurRegionsChange blur_regions = 22;
- TrustedOverlayChange trusted_overlay = 23;
- }
-}
-
-message PositionChange {
- required float x = 1;
- required float y = 2;
-}
-
-message SizeChange {
- required uint32 w = 1;
- required uint32 h = 2;
-}
-
-message AlphaChange {
- required float alpha = 1;
-}
-
-message CornerRadiusChange {
- required float corner_radius = 1;
-}
-
-message BackgroundBlurRadiusChange {
- required float background_blur_radius = 1;
-}
-
-message LayerChange {
- required uint32 layer = 1;
-}
-
-message CropChange {
- required Rectangle rectangle = 1;
-}
-
-message MatrixChange {
- required float dsdx = 1;
- required float dtdx = 2;
- required float dsdy = 3;
- required float dtdy = 4;
-}
-
-message TransparentRegionHintChange {
- repeated Rectangle region = 1;
-}
-
-message LayerStackChange {
- required uint32 layer_stack = 1;
-}
-
-message DisplayFlagsChange {
- required uint32 flags = 1;
-}
-
-message HiddenFlagChange {
- required bool hidden_flag = 1;
-}
-
-message OpaqueFlagChange {
- required bool opaque_flag = 1;
-}
-
-message SecureFlagChange {
- required bool secure_flag = 1;
-}
-
-message DisplayChange {
- required int32 id = 1;
-
- oneof DisplayChange {
- DispSurfaceChange surface = 2;
- LayerStackChange layer_stack = 3;
- SizeChange size = 4;
- ProjectionChange projection = 5;
- DisplayFlagsChange flags = 6;
- }
-}
-
-message DispSurfaceChange {
- required uint64 buffer_queue_id = 1;
- required string buffer_queue_name = 2;
-}
-
-message ProjectionChange {
- required int32 orientation = 1;
- required Rectangle viewport = 2;
- required Rectangle frame = 3;
-}
-
-message Rectangle {
- required int32 left = 1;
- required int32 top = 2;
- required int32 right = 3;
- required int32 bottom = 4;
-}
-
-message SurfaceCreation {
- required int32 id = 1;
- required string name = 2;
- required uint32 w = 3;
- required uint32 h = 4;
-}
-
-message SurfaceDeletion {
- required int32 id = 1;
-}
-
-message BufferUpdate {
- required int32 id = 1;
- required uint32 w = 2;
- required uint32 h = 3;
- required uint64 frame_number = 4;
-}
-
-message VSyncEvent {
- required int64 when = 1;
-}
-
-message DisplayCreation {
- required int32 id = 1;
- required string name = 2;
- optional uint64 display_id = 3;
- required bool is_secure = 4;
-}
-
-message DisplayDeletion {
- required int32 id = 1;
-}
-
-message PowerModeUpdate {
- required int32 id = 1;
- required int32 mode = 2;
-}
-
-message ReparentChange {
- required int32 parent_id = 1;
-}
-
-message RelativeParentChange {
- required int32 relative_parent_id = 1;
- required int32 z = 2;
-}
-
-message ShadowRadiusChange {
- required float radius = 1;
-}
-
-message TrustedOverlayChange {
- required float is_trusted_overlay = 1;
-}
-
-message BlurRegionsChange {
- repeated BlurRegionChange blur_regions = 1;
-}
-
-message BlurRegionChange {
- required uint32 blur_radius = 1;
- required float corner_radius_tl = 2;
- required float corner_radius_tr = 3;
- required float corner_radius_bl = 4;
- required float corner_radius_br = 5;
- required float alpha = 6;
- required int32 left = 7;
- required int32 top = 8;
- required int32 right = 9;
- required int32 bottom = 10;
-}
-
-message Origin {
- required int32 pid = 1;
- required int32 uid = 2;
-}
diff --git a/cmds/surfacereplayer/replayer/Android.bp b/cmds/surfacereplayer/replayer/Android.bp
deleted file mode 100644
index 3985230..0000000
--- a/cmds/surfacereplayer/replayer/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-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_shared {
- name: "libsurfacereplayer",
- srcs: [
- "BufferQueueScheduler.cpp",
- "Event.cpp",
- "Replayer.cpp",
- ],
- cppflags: [
- "-Werror",
- "-Wno-unused-parameter",
- "-Wno-format",
- "-Wno-c++98-compat-pedantic",
- "-Wno-float-conversion",
- "-Wno-disabled-macro-expansion",
- "-Wno-float-equal",
- "-Wno-sign-conversion",
- "-Wno-padded",
- ],
- static_libs: [
- "libtrace_proto",
- ],
- shared_libs: [
- "libEGL",
- "libGLESv2",
- "libbinder",
- "liblog",
- "libcutils",
- "libgui",
- "libui",
- "libutils",
- "libprotobuf-cpp-lite",
- "libbase",
- "libnativewindow",
- ],
- export_include_dirs: [
- ".",
- ],
-}
-
-cc_binary {
- name: "surfacereplayer",
- srcs: [
- "Main.cpp",
- ],
- shared_libs: [
- "libprotobuf-cpp-lite",
- "libsurfacereplayer",
- "libutils",
- "libgui",
- ],
- static_libs: [
- "libtrace_proto",
- ],
- cppflags: [
- "-Werror",
- "-Wno-unused-parameter",
- "-Wno-c++98-compat-pedantic",
- "-Wno-float-conversion",
- "-Wno-disabled-macro-expansion",
- "-Wno-float-equal",
- ],
-}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
deleted file mode 100644
index 77de8dc..0000000
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2016 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 "BufferQueueScheduler"
-
-#include "BufferQueueScheduler.h"
-
-#include <android/native_window.h>
-#include <gui/Surface.h>
-
-using namespace android;
-
-BufferQueueScheduler::BufferQueueScheduler(
- const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
- : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
-
-void BufferQueueScheduler::startScheduling() {
- ALOGV("Starting Scheduler for %d Layer", mSurfaceId);
- std::unique_lock<std::mutex> lock(mMutex);
- if (mSurfaceControl == nullptr) {
- mCondition.wait(lock, [&] { return (mSurfaceControl != nullptr); });
- }
-
- while (mContinueScheduling) {
- while (true) {
- if (mBufferEvents.empty()) {
- break;
- }
-
- BufferEvent event = mBufferEvents.front();
- lock.unlock();
-
- bufferUpdate(event.dimensions);
- fillSurface(event.event);
- mColor.modulate();
- lock.lock();
- mBufferEvents.pop();
- }
- mCondition.wait(lock);
- }
-}
-
-void BufferQueueScheduler::addEvent(const BufferEvent& event) {
- std::lock_guard<std::mutex> lock(mMutex);
- mBufferEvents.push(event);
- mCondition.notify_one();
-}
-
-void BufferQueueScheduler::stopScheduling() {
- std::lock_guard<std::mutex> lock(mMutex);
- mContinueScheduling = false;
- mCondition.notify_one();
-}
-
-void BufferQueueScheduler::setSurfaceControl(
- const sp<SurfaceControl>& surfaceControl, const HSV& color) {
- std::lock_guard<std::mutex> lock(mMutex);
- mSurfaceControl = surfaceControl;
- mColor = color;
- mCondition.notify_one();
-}
-
-void BufferQueueScheduler::bufferUpdate(const Dimensions& dimensions) {
- sp<Surface> s = mSurfaceControl->getSurface();
- s->setBuffersDimensions(dimensions.width, dimensions.height);
-}
-
-void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
- ANativeWindow_Buffer outBuffer;
- sp<Surface> s = mSurfaceControl->getSurface();
-
- status_t status = s->lock(&outBuffer, nullptr);
-
- if (status != NO_ERROR) {
- ALOGE("fillSurface: failed to lock buffer, (%d)", status);
- return;
- }
-
- auto color = mColor.getRGB();
-
- auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; y++) {
- for (int x = 0; x < outBuffer.width; x++) {
- uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
- pixel[0] = color.r;
- pixel[1] = color.g;
- pixel[2] = color.b;
- pixel[3] = LAYER_ALPHA;
- }
- }
-
- event->readyToExecute();
-
- status = s->unlockAndPost();
-
- ALOGE_IF(status != NO_ERROR, "fillSurface: failed to unlock and post buffer, (%d)", status);
-}
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
deleted file mode 100644
index cb20fcc..0000000
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
-#define ANDROID_SURFACEREPLAYER_BUFFERQUEUESCHEDULER_H
-
-#include "Color.h"
-#include "Event.h"
-
-#include <gui/SurfaceControl.h>
-
-#include <utils/StrongPointer.h>
-
-#include <atomic>
-#include <condition_variable>
-#include <mutex>
-#include <queue>
-#include <utility>
-
-namespace android {
-
-auto constexpr LAYER_ALPHA = 190;
-
-struct Dimensions {
- Dimensions() = default;
- Dimensions(int w, int h) : width(w), height(h) {}
-
- int width = 0;
- int height = 0;
-};
-
-struct BufferEvent {
- BufferEvent() = default;
- BufferEvent(std::shared_ptr<Event> e, Dimensions d) : event(e), dimensions(d) {}
-
- std::shared_ptr<Event> event;
- Dimensions dimensions;
-};
-
-class BufferQueueScheduler {
- public:
- BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
-
- void startScheduling();
- void addEvent(const BufferEvent&);
- void stopScheduling();
-
- void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
-
- private:
- void bufferUpdate(const Dimensions& dimensions);
-
- // Lock and fill the surface, block until the event is signaled by the main loop,
- // then unlock and post the buffer.
- void fillSurface(const std::shared_ptr<Event>& event);
-
- sp<SurfaceControl> mSurfaceControl;
- HSV mColor;
- const int mSurfaceId;
-
- bool mContinueScheduling;
-
- std::queue<BufferEvent> mBufferEvents;
- std::mutex mMutex;
- std::condition_variable mCondition;
-};
-
-} // namespace android
-#endif
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
deleted file mode 100644
index ce644be..0000000
--- a/cmds/surfacereplayer/replayer/Color.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_COLOR_H
-#define ANDROID_SURFACEREPLAYER_COLOR_H
-
-#include <cmath>
-#include <cstdlib>
-
-namespace android {
-
-constexpr double modulateFactor = .0001;
-constexpr double modulateLimit = .80;
-
-struct RGB {
- RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
-
- uint8_t r = 0;
- uint8_t g = 0;
- uint8_t b = 0;
-};
-
-struct HSV {
- HSV() = default;
- HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
-
- double h = 0;
- double s = 0;
- double v = 0;
-
- RGB getRGB() const;
-
- bool modulateUp = false;
-
- void modulate();
-};
-
-void inline HSV::modulate() {
- if(modulateUp) {
- v += modulateFactor;
- } else {
- v -= modulateFactor;
- }
-
- if(v <= modulateLimit || v >= 1) {
- modulateUp = !modulateUp;
- }
-}
-
-inline RGB HSV::getRGB() const {
- using namespace std;
- double r = 0, g = 0, b = 0;
-
- if (s == 0) {
- r = v;
- g = v;
- b = v;
- } else {
- auto tempHue = static_cast<int>(h) % 360;
- tempHue = tempHue / 60;
-
- int i = static_cast<int>(trunc(tempHue));
- double f = h - i;
-
- double x = v * (1.0 - s);
- double y = v * (1.0 - (s * f));
- double z = v * (1.0 - (s * (1.0 - f)));
-
- switch (i) {
- case 0:
- r = v;
- g = z;
- b = x;
- break;
-
- case 1:
- r = y;
- g = v;
- b = x;
- break;
-
- case 2:
- r = x;
- g = v;
- b = z;
- break;
-
- case 3:
- r = x;
- g = y;
- b = v;
- break;
-
- case 4:
- r = z;
- g = x;
- b = v;
- break;
-
- default:
- r = v;
- g = x;
- b = y;
- break;
- }
- }
-
- return RGB(round(r * 255), round(g * 255), round(b * 255));
-}
-}
-#endif
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
deleted file mode 100644
index 64db5f0..0000000
--- a/cmds/surfacereplayer/replayer/Event.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2016 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 "Event.h"
-
-using namespace android;
-using Increment = surfaceflinger::Increment;
-
-Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
-
-void Event::readyToExecute() {
- changeState(Event::EventState::Waiting);
- waitUntil(Event::EventState::Signaled);
- changeState(Event::EventState::Running);
-}
-
-void Event::complete() {
- waitUntil(Event::EventState::Waiting);
- changeState(Event::EventState::Signaled);
- waitUntil(Event::EventState::Running);
-}
-
-void Event::waitUntil(Event::EventState state) {
- std::unique_lock<std::mutex> lock(mLock);
- mCond.wait(lock, [this, state] { return (mState == state); });
-}
-
-void Event::changeState(Event::EventState state) {
- std::unique_lock<std::mutex> lock(mLock);
- mState = state;
- lock.unlock();
-
- mCond.notify_one();
-}
-
-Increment::IncrementCase Event::getIncrementType() {
- return mIncrementType;
-}
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
deleted file mode 100644
index 09a7c24..0000000
--- a/cmds/surfacereplayer/replayer/Event.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_EVENT_H
-#define ANDROID_SURFACEREPLAYER_EVENT_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <condition_variable>
-#include <mutex>
-
-namespace android {
-
-using Increment = surfaceflinger::Increment;
-
-class Event {
- public:
- Event(Increment::IncrementCase);
-
- enum class EventState {
- SettingUp, // Completing as much time-independent work as possible
- Waiting, // Waiting for signal from main thread to finish execution
- Signaled, // Signaled by main thread, about to immediately switch to Running
- Running // Finishing execution of rest of work
- };
-
- void readyToExecute();
- void complete();
-
- Increment::IncrementCase getIncrementType();
-
- private:
- void waitUntil(EventState state);
- void changeState(EventState state);
-
- std::mutex mLock;
- std::condition_variable mCond;
-
- EventState mState = EventState::SettingUp;
-
- Increment::IncrementCase mIncrementType;
-};
-}
-#endif
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
deleted file mode 100644
index fbfcacf..0000000
--- a/cmds/surfacereplayer/replayer/Main.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-
-/*
- * Replayer - Main.cpp
- *
- * 1. Get flags from command line
- * 2. Commit actions or settings based on the flags
- * 3. Initalize a replayer object with the filename passed in
- * 4. Replay
- * 5. Exit successfully or print error statement
- */
-
-#include <Replayer.h>
-
-#include <csignal>
-#include <iostream>
-#include <stdlib.h>
-#include <unistd.h>
-
-using namespace android;
-
-void printHelpMenu() {
- std::cout << "SurfaceReplayer options:\n";
- std::cout << "Usage: surfacereplayer [OPTIONS...] <TRACE FILE>\n";
- std::cout << " File path must be absolute" << std::endl << std::endl;
-
- std::cout << " -m Stops the replayer at the start of the trace and switches ";
- "to manual replay\n";
-
- std::cout << "\n -t [Number of Threads] Specifies the number of threads to be used while "
- "replaying (default is " << android::DEFAULT_THREADS << ")\n";
-
- std::cout << "\n -s [Timestamp] Specify at what timestamp should the replayer switch "
- "to manual replay\n";
-
- std::cout << " -n Ignore timestamps and run through trace as fast as possible\n";
-
- std::cout << " -l Indefinitely loop the replayer\n";
-
- std::cout << " -h Display help menu\n";
-
- std::cout << std::endl;
-}
-
-int main(int argc, char** argv) {
- std::string filename;
- bool loop = false;
- bool wait = true;
- bool pauseBeginning = false;
- int numThreads = DEFAULT_THREADS;
- long stopHere = -1;
-
- int opt = 0;
- while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
- switch (opt) {
- case 'm':
- pauseBeginning = true;
- break;
- case 't':
- numThreads = atoi(optarg);
- break;
- case 's':
- stopHere = atol(optarg);
- break;
- case 'n':
- wait = false;
- break;
- case 'l':
- loop = true;
- break;
- case 'h':
- case '?':
- printHelpMenu();
- exit(0);
- default:
- std::cerr << "Invalid argument...exiting" << std::endl;
- printHelpMenu();
- exit(0);
- }
- }
-
- char** input = argv + optind;
- if (input[0] == nullptr) {
- std::cerr << "No trace file provided...exiting" << std::endl;
- abort();
- }
- filename.assign(input[0]);
-
- status_t status = NO_ERROR;
- do {
- android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
- status = r.replay();
- } while(loop);
-
- if (status == NO_ERROR) {
- std::cout << "Successfully finished replaying trace" << std::endl;
- } else {
- std::cerr << "Trace replayer returned error: " << status << std::endl;
- }
-
- return 0;
-}
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
deleted file mode 100644
index 893f0dc..0000000
--- a/cmds/surfacereplayer/replayer/README.md
+++ /dev/null
@@ -1,262 +0,0 @@
-SurfaceReplayer Documentation
-===================
-
-[go/SurfaceReplayer](go/SurfaceReplayer)
-
-SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
-[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
-
-* Creation and deletion of surfaces/displays
-* Alterations to the surfaces/displays called Transactions
-* Buffer Updates to surfaces
-* VSync events
-
-At their specified times to be as close to the original trace.
-
-Usage
---------
-
-###Creating a trace
-
-SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
-utilize it. To allow it to write to the device, run
-
-`setenforce 0`
-
-To start recording a trace, run
-
-`service call SurfaceFlinger 1020 i32 1`
-
-To stop recording, run
-
-`service call SurfaceFlinger 1020 i32 0`
-
-The default location for the trace is `/data/SurfaceTrace.dat`
-
-###Executable
-
-To replay a specific trace, execute
-
-`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
-
-inside the android shell. This will replay the full trace and then exit. Running this command
-outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
-off VSync injections if it interrupted in any way other than fully replaying the trace
-
-The replay will not fill surfaces with their contents during the capture. Rather they are given a
-random color which will be the same every time the trace is replayed. Surfaces modulate their color
-at buffer updates.
-
-**Options:**
-
-- -m pause the replayer at the start of the trace for manual replay
-- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
-- -s [Timestamp] switches to manual replay at specified timestamp
-- -n Ignore timestamps and run through trace as fast as possible
-- -l Indefinitely loop the replayer
-- -h displays help menu
-
-**Manual Replay:**
-When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
-by the user. Pressing CTRL-C again will exit the replayer.
-
-Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
-input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
-without inputting a command repeats the previous command.
-
-- n - steps the replayer to the next VSync event
-- ni - steps the replayer to the next increment
-- c - continues normal replaying
-- c [milliseconds] - continue until specified number of milliseconds have passed
-- s [timestamp] - continue and stop at specified timestamp
-- l - list out timestamp of current increment
-- h - displays help menu
-
-###Shared Library
-
-To use the shared library include these shared libraries
-
-`libsurfacereplayer`
-`libprotobuf-cpp-full`
-`libutils`
-
-And the static library
-
-`libtrace_proto`
-
-Include the replayer header at the top of your file
-
-`#include <replayer/Replayer.h>`
-
-There are two constructors for the replayer
-
-`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
-`Replayer(Trace& trace, ... ditto ...)`
-
-The first constructor takes in the filepath where the trace is located and loads in the trace
-object internally.
-- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
-- numThreads - Number of worker threads the replayer will use.
-- wait - **False**: Replayer ignores waits in between increments
-- stopHere - Time stamp of where the replayer should run to then switch to manual replay
-
-The second constructor includes all of the same parameters but takes in a preloaded trace object.
-To use add
-
-`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
-
-To your file
-
-After initializing the Replayer call
-
- replayer.replay();
-
-And the trace will start replaying. Once the trace is finished replaying, the function will return.
-The layers that are visible at the end of the trace will remain on screen until the program
-terminates.
-
-
-**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
-never executed. This can be fixed by executing
-
-`service call SurfaceFlinger 23 i32 0`
-
-in the android shell
-
-Code Breakdown
--------------
-
-The Replayer is composed of 5 components.
-
-- The data format of the trace (Trace.proto)
-- The Replayer object (Replayer.cpp)
-- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
-- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
-- The Main executable (Main.cpp)
-
-### Traces
-
-Traces are represented as a protobuf message located in surfacereplayer/proto/src.
-
-**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
-**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
-
- - Transaction
- - SurfaceCreation
- - SurfaceDeletion
- - DisplayCreation
- - DisplayDeleteion
- - BufferUpdate
- - VSyncEvent
- - PowerModeUpdate
-
-**Transactions** contain whether the transaction was synchronous or animated and *repeated*
-**SurfaceChanges** and **DisplayChanges**
-
-- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
-position, alpha, hidden, size, etc.
-- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
-size, layer stack, projection, etc.
-
-**Surface/Display Creation** contain the id of the surface/display and the name of the
-surface/display
-
-**Surface/Display Deletion** contain the id of the surface/display to be deleted
-
-**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
-buffer, and the frame number.
-
-**VSyncEvents** contain when the VSync event has occurred.
-
-**PowerModeUpdates** contain the id of the display being updated and what mode it is being
-changed to.
-
-To output the contents of a trace in a readable format, execute
-
-`**aprotoc** --decode=Trace \
--I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
-$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
- < **YourTraceFile.dat** > **YourOutputName.txt**`
-
-
-###Replayer
-
-Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
-amount of time until the increment should be executed, then executing the increment. The first
-increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
-goes from there.
-
-Increments from the trace are played asynchronously rather than one by one, being dispatched by
-the main thread, queued up in a thread pool and completed when the main thread deems they are
-ready to finish execution.
-
-When an increment is dispatched, it completes as much work as it can before it has to be
-synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
-(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
-object. The main thread holds a queue of these Event objects and completes the
-corresponding Event base on its time stamp. After completing an increment, the main thread will
-dispatch another increment and continue.
-
-The main thread's execution flow is outlined below
-
- initReplay() //queue up the initial increments
- while(!pendingIncrements.empty()) { //while increments remaining
- event = pendingIncrement.pop();
- wait(event.time_stamp(); //waitUntil it is time to complete this increment
-
- event.complete() //signal to let event finish
- if(increments remaing()) {
- dispatchEvent() //queue up another increment
- }
- }
-
-A worker thread's flow looks like so
-
- //dispatched!
- Execute non-time sensitive work here
- ...
- event.readyToExecute() //time sensitive point...waiting for Main Thread
- ...
- Finish execution
-
-
-### Event
-
-An Event is a simple synchronization mechanism used to facilitate communication between the main
-and worker threads. Every time an increment is dispatched, an Event object is also created.
-
-An Event can be in 4 different states:
-
-- **SettingUp** - The worker is in the process of completing all non-time sensitive work
-- **Waiting** - The worker is waiting on the main thread to signal it.
-- **Signaled** - The worker has just been signaled by the main thread
-- **Running** - The worker is running again and finishing the rest of its work.
-
-When the main thread wants to finish the execution of a worker, the worker can either still be
-**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
-main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
-state once **Signaled**. This last step exists in order to communicate back to the main thread that
-the worker thread has actually started completing its execution, rather than being preempted right
-after signalling. Once this happens, the main thread schedules the next worker. This makes sure
-there is a constant amount of workers running at one time.
-
-This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
-worker and main thread respectively.
-
-### BufferQueueScheduler
-
-During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
-buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
-**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
-which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
-handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
-
-When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
-**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
-every time an Event is completed.
-
-### Main
-
-The main exectuable reads in the command line arguments. Creates the Replayer using those
-arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
-gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
deleted file mode 100644
index d62522a..0000000
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEREPLAYER_H
-#define ANDROID_SURFACEREPLAYER_H
-
-#include "BufferQueueScheduler.h"
-#include "Color.h"
-#include "Event.h"
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceControl.h>
-
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-#include <stdatomic.h>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <unordered_map>
-#include <utility>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
-const auto RAND_COLOR_SEED = 700;
-const auto DEFAULT_THREADS = 3;
-
-typedef int32_t layer_id;
-typedef int32_t display_id;
-
-typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
-typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
-
-class Replayer {
- public:
- Replayer(const std::string& filename, bool replayManually = false,
- int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
- Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
- bool wait = true, nsecs_t stopHere = -1);
-
- status_t replay();
-
- private:
- status_t initReplay();
-
- void waitForConsoleCommmand();
- static void stopAutoReplayHandler(int signal);
-
- status_t dispatchEvent(int index);
-
- status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
- status_t createSurfaceControl(const SurfaceCreation& create,
- const std::shared_ptr<Event>& event);
- status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
- void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
- void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
- void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
-
- status_t doSurfaceTransaction(SurfaceComposerClient::Transaction& transaction,
- const SurfaceChanges& surfaceChange);
- void doDisplayTransaction(SurfaceComposerClient::Transaction& transaction,
- const DisplayChanges& displayChange);
-
- void setPosition(SurfaceComposerClient::Transaction& t,
- layer_id id, const PositionChange& pc);
- void setSize(SurfaceComposerClient::Transaction& t,
- layer_id id, const SizeChange& sc);
- void setAlpha(SurfaceComposerClient::Transaction& t,
- layer_id id, const AlphaChange& ac);
- void setLayer(SurfaceComposerClient::Transaction& t,
- layer_id id, const LayerChange& lc);
- void setCrop(SurfaceComposerClient::Transaction& t,
- layer_id id, const CropChange& cc);
- void setCornerRadius(SurfaceComposerClient::Transaction& t,
- layer_id id, const CornerRadiusChange& cc);
- void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
- layer_id id, const BackgroundBlurRadiusChange& cc);
- void setBlurRegions(SurfaceComposerClient::Transaction& t,
- layer_id id, const BlurRegionsChange& cc);
- void setMatrix(SurfaceComposerClient::Transaction& t,
- layer_id id, const MatrixChange& mc);
- void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
- layer_id id, const TransparentRegionHintChange& trgc);
- void setLayerStack(SurfaceComposerClient::Transaction& t,
- layer_id id, const LayerStackChange& lsc);
- void setHiddenFlag(SurfaceComposerClient::Transaction& t,
- layer_id id, const HiddenFlagChange& hfc);
- void setOpaqueFlag(SurfaceComposerClient::Transaction& t,
- layer_id id, const OpaqueFlagChange& ofc);
- void setSecureFlag(SurfaceComposerClient::Transaction& t,
- layer_id id, const SecureFlagChange& sfc);
- void setReparentChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ReparentChange& c);
- void setRelativeParentChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const RelativeParentChange& c);
- void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ShadowRadiusChange& c);
- void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const BlurRegionsChange& c);
-
- void setDisplaySurface(SurfaceComposerClient::Transaction& t,
- display_id id, const DispSurfaceChange& dsc);
- void setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
- display_id id, const LayerStackChange& lsc);
- void setDisplaySize(SurfaceComposerClient::Transaction& t,
- display_id id, const SizeChange& sc);
- void setDisplayProjection(SurfaceComposerClient::Transaction& t,
- display_id id, const ProjectionChange& pc);
-
- void waitUntilTimestamp(int64_t timestamp);
- status_t loadSurfaceComposerClient();
-
- Trace mTrace;
- bool mLoaded = false;
- int32_t mIncrementIndex = 0;
- int64_t mCurrentTime = 0;
- int32_t mNumThreads = DEFAULT_THREADS;
-
- Increment mCurrentIncrement;
-
- std::string mLastInput;
-
- static atomic_bool sReplayingManually;
- bool mWaitingForNextVSync;
- bool mWaitForTimeStamps;
- nsecs_t mStopTimeStamp;
- bool mHasStopped;
-
- std::mutex mLayerLock;
- std::condition_variable mLayerCond;
- std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
- std::unordered_map<layer_id, HSV> mColors;
-
- std::mutex mPendingLayersLock;
- std::vector<layer_id> mLayersPendingRemoval;
-
- std::mutex mBufferQueueSchedulerLock;
- std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
-
- std::mutex mDisplayLock;
- std::condition_variable mDisplayCond;
- std::unordered_map<display_id, sp<IBinder>> mDisplays;
-
- sp<SurfaceComposerClient> mComposerClient;
- std::queue<std::shared_ptr<Event>> mPendingIncrements;
-};
-
-} // namespace android
-#endif
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
deleted file mode 100644
index 58bfbf3..0000000
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/usr/bin/python
-from subprocess import call
-import os
-proto_path = os.environ['ANDROID_BUILD_TOP'] + "/frameworks/native/cmds/surfacereplayer/proto/src/"
-call(["aprotoc", "-I=" + proto_path, "--python_out=.", proto_path + "trace.proto"])
-
-from trace_pb2 import *
-
-trace = Trace()
-
-def main():
- global trace
- while(1):
- option = main_menu()
-
- if option == 0:
- break
-
- increment = trace.increment.add()
- increment.time_stamp = int(input("Time stamp of action: "))
-
- if option == 1:
- transaction(increment)
- elif option == 2:
- surface_create(increment)
- elif option == 3:
- surface_delete(increment)
- elif option == 4:
- display_create(increment)
- elif option == 5:
- display_delete(increment)
- elif option == 6:
- buffer_update(increment)
- elif option == 7:
- vsync_event(increment)
- elif option == 8:
- power_mode_update(increment)
-
- seralizeTrace()
-
-def seralizeTrace():
- with open("trace.dat", 'wb') as f:
- f.write(trace.SerializeToString())
-
-
-def main_menu():
- print ("")
- print ("What would you like to do?")
- print ("1. Add transaction")
- print ("2. Add surface creation")
- print ("3. Add surface deletion")
- print ("4. Add display creation")
- print ("5. Add display deletion")
- print ("6. Add buffer update")
- print ("7. Add VSync event")
- print ("8. Add power mode update")
- print ("0. Finish and serialize")
- print ("")
-
- return int(input("> "))
-
-def transaction_menu():
- print ("")
- print ("What kind of transaction?")
- print ("1. Position Change")
- print ("2. Size Change")
- print ("3. Alpha Change")
- print ("4. Layer Change")
- print ("5. Crop Change")
- print ("6. Final Crop Change")
- print ("7. Matrix Change")
- print ("9. Transparent Region Hint Change")
- print ("10. Layer Stack Change")
- print ("11. Hidden Flag Change")
- print ("12. Opaque Flag Change")
- print ("13. Secure Flag Change")
- print ("14. Deferred Transaction Change")
- print ("15. Display - Surface Change")
- print ("16. Display - Layer Stack Change")
- print ("17. Display - Size Change")
- print ("18. Display - Projection Change")
- print ("0. Finished adding Changes to this transaction")
- print ("")
-
- return int(input("> "))
-
-def transaction(increment):
- global trace
-
- increment.transaction.synchronous \
- = bool(input("Is transaction synchronous (True/False): "))
- increment.transaction.animation \
- = bool(input("Is transaction animated (True/False): "))
-
- while(1):
- option = transaction_menu()
-
- if option == 0:
- break
-
- change = None
- if option <= 14:
- change = increment.transaction.surface_change.add()
- elif option >= 15 and option <= 18:
- change = increment.transaction.display_change.add()
-
- change.id = int(input("ID of layer/display to undergo a change: "))
-
- if option == 1:
- change.position.x, change.position.y = position()
- elif option == 2:
- change.size.w, change.size.h = size()
- elif option == 3:
- change.alpha.alpha = alpha()
- elif option == 4:
- change.layer.layer = layer()
- elif option == 5:
- change.crop.rectangle.left, change.crop.rectangle.top, \
- change.crop.rectangle.right, change.crop.rectangle.bottom = crop()
- elif option == 6:
- change.final_crop.rectangle.left, \
- change.final_crop.rectangle.top, \
- change.final_crop.rectangle.right,\
- change.final_crop.rectangle.bottom = final_crop()
- elif option == 7:
- change.matrix.dsdx,\
- change.matrix.dtdx,\
- change.matrix.dsdy,\
- change.matrix.dtdy = layer()
- elif option == 9:
- for rect in transparent_region_hint():
- new = increment.transparent_region_hint.region.add()
- new.left = rect[0]
- new.top = rect[1]
- new.right = rect[2]
- new.bottom = rect[3]
- elif option == 10:
- change.layer_stack.layer_stack = layer_stack()
- elif option == 11:
- change.hidden_flag.hidden_flag = hidden_flag()
- elif option == 12:
- change.opaque_flag.opaque_flag = opaque_flag()
- elif option == 13:
- change.secure_flag.secure_flag = secure_flag()
- elif option == 14:
- change.deferred_transaction.layer_id, \
- change.deferred_transaction.frame_number = deferred_transaction()
- elif option == 15:
- change.surface.buffer_queue_id, \
- change.surface.buffer_queue_name = surface()
- elif option == 16:
- change.layer_stack.layer_stack = layer_stack()
- elif option == 17:
- change.size.w, change.size.h = size()
- elif option == 18:
- projection(change)
-
-def surface_create(increment):
- increment.surface_creation.id = int(input("Enter id: "))
- n = str(raw_input("Enter name: "))
- increment.surface_creation.name = n
- increment.surface_creation.w = input("Enter w: ")
- increment.surface_creation.h = input("Enter h: ")
-
-def surface_delete(increment):
- increment.surface_deletion.id = int(input("Enter id: "))
-
-def display_create(increment):
- increment.display_creation.id = int(input("Enter id: "))
- increment.display_creation.name = str(raw_input("Enter name: "))
- increment.display_creation.display_id = int(input("Enter display ID: "))
- increment.display_creation.is_secure = bool(input("Enter if secure: "))
-
-def display_delete(increment):
- increment.surface_deletion.id = int(input("Enter id: "))
-
-def buffer_update(increment):
- increment.buffer_update.id = int(input("Enter id: "))
- increment.buffer_update.w = int(input("Enter w: "))
- increment.buffer_update.h = int(input("Enter h: "))
- increment.buffer_update.frame_number = int(input("Enter frame_number: "))
-
-def vsync_event(increment):
- increment.vsync_event.when = int(input("Enter when: "))
-
-def power_mode_update(increment):
- increment.power_mode_update.id = int(input("Enter id: "))
- increment.power_mode_update.mode = int(input("Enter mode: "))
-
-def position():
- x = input("Enter x: ")
- y = input("Enter y: ")
-
- return float(x), float(y)
-
-def size():
- w = input("Enter w: ")
- h = input("Enter h: ")
-
- return int(w), int(h)
-
-def alpha():
- alpha = input("Enter alpha: ")
-
- return float(alpha)
-
-def layer():
- layer = input("Enter layer: ")
-
- return int(layer)
-
-def crop():
- return rectangle()
-
-def final_crop():
- return rectangle()
-
-def matrix():
- dsdx = input("Enter dsdx: ")
- dtdx = input("Enter dtdx: ")
- dsdy = input("Enter dsdy: ")
- dtdy = input("Enter dtdy: ")
-
- return float(dsdx)
-
-def transparent_region_hint():
- num = input("Enter number of rectangles in region: ")
-
- return [rectangle() in range(x)]
-
-def layer_stack():
- layer_stack = input("Enter layer stack: ")
-
- return int(layer_stack)
-
-def hidden_flag():
- flag = input("Enter hidden flag state (True/False): ")
-
- return bool(flag)
-
-def opaque_flag():
- flag = input("Enter opaque flag state (True/False): ")
-
- return bool(flag)
-
-def secure_flag():
- flag = input("Enter secure flag state (True/False): ")
-
- return bool(flag)
-
-def deferred_transaction():
- layer_id = input("Enter layer_id: ")
- frame_number = input("Enter frame_number: ")
-
- return int(layer_id), int(frame_number)
-
-def surface():
- id = input("Enter id: ")
- name = raw_input("Enter name: ")
-
- return int(id), str(name)
-
-def projection(change):
- change.projection.orientation = input("Enter orientation: ")
- print("Enter rectangle for viewport")
- change.projection.viewport.left, \
- change.projection.viewport.top, \
- change.projection.viewport.right,\
- change.projection.viewport.bottom = rectangle()
- print("Enter rectangle for frame")
- change.projection.frame.left, \
- change.projection.frame.top, \
- change.projection.frame.right,\
- change.projection.frame.bottom = rectangle()
-
-def rectangle():
- left = input("Enter left: ")
- top = input("Enter top: ")
- right = input("Enter right: ")
- bottom = input("Enter bottom: ")
-
- return int(left), int(top), int(right), int(bottom)
-
-if __name__ == "__main__":
- main()
diff --git a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml
new file mode 100644
index 0000000..d0b594c
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2023-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="132580097" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2023-03-01.xml
new file mode 100644
index 0000000..6ae248a
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2023-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="132580097" />
+</permissions>
diff --git a/include/android/font.h b/include/android/font.h
index 8a3a474..0225725 100644
--- a/include/android/font.h
+++ b/include/android/font.h
@@ -31,6 +31,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <sys/cdefs.h>
/******************************************************************
@@ -86,10 +87,11 @@
AFONT_WEIGHT_MAX = 1000
};
+struct AFont;
/**
* AFont provides information of the single font configuration.
*/
-struct AFont;
+typedef struct AFont AFont;
/**
* Close an AFont.
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h
index 4417422..60ff95e 100644
--- a/include/android/font_matcher.h
+++ b/include/android/font_matcher.h
@@ -75,6 +75,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <android/font.h>
@@ -116,11 +117,12 @@
AFAMILY_VARIANT_ELEGANT = 2,
};
+struct AFontMatcher;
/**
* AFontMatcher performs match operation on given parameters and available font files.
* This matcher is not a thread-safe object. Do not pass this matcher to other threads.
*/
-struct AFontMatcher;
+typedef struct AFontMatcher AFontMatcher;
/**
* Select the best font from given parameters.
diff --git a/include/android/input.h b/include/android/input.h
index 7080386..5d19c5c 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -771,9 +771,33 @@
* The interpretation of a generic axis is device-specific.
*/
AMOTION_EVENT_AXIS_GENERIC_16 = 47,
+ /**
+ * Axis constant: X gesture offset axis of a motion event.
+ *
+ * - For a touch pad, reports the distance that a swipe gesture has moved in the X axis, as a
+ * proportion of the touch pad's size. For example, if a touch pad is 1000 units wide, and a
+ * swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of
+ * -0.1.
+ */
+ AMOTION_EVENT_AXIS_GESTURE_X_OFFSET = 48,
+ /**
+ * Axis constant: Y gesture offset axis of a motion event.
+ *
+ * The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis.
+ */
+ AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET = 49,
+
+ /**
+ * Note: This is not an "Axis constant". It does not represent any axis, nor should it be used
+ * to represent any axis. It is a constant holding the value of the largest defined axis value,
+ * to make some computations (like iterating through all possible axes) cleaner.
+ * Please update the value accordingly if you add a new axis.
+ */
+ AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET,
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
+ // Update AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE accordingly as well.
};
/**
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 5fa47f6..eed6b33 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -88,6 +88,36 @@
typedef struct APerformanceHintSession APerformanceHintSession;
/**
+ * Hints for the session used by {@link APerformanceHint_sendHint} to signal upcoming changes
+ * in the mode or workload.
+ */
+enum SessionHint {
+ /**
+ * This hint indicates a sudden increase in CPU workload intensity. It means
+ * that this hint session needs extra CPU resources immediately to meet the
+ * target duration for the current work cycle.
+ */
+ CPU_LOAD_UP = 0,
+ /**
+ * This hint indicates a decrease in CPU workload intensity. It means that
+ * this hint session can reduce CPU resources and still meet the target duration.
+ */
+ CPU_LOAD_DOWN = 1,
+ /*
+ * This hint indicates an upcoming CPU workload that is completely changed and
+ * unknown. It means that the hint session should reset CPU resources to a known
+ * baseline to prepare for an arbitrary load, and must wake up if inactive.
+ */
+ CPU_LOAD_RESET = 2,
+ /*
+ * This hint indicates that the most recent CPU workload is resuming after a
+ * period of inactivity. It means that the hint session should allocate similar
+ * CPU resources to what was used previously, and must wake up if inactive.
+ */
+ CPU_LOAD_RESUME = 3,
+};
+
+/**
* Acquire an instance of the performance hint manager.
*
* @return manager instance on success, nullptr on failure.
@@ -159,6 +189,17 @@
void APerformanceHint_closeSession(
APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__);
+/**
+ * Sends performance hints to inform the hint session of changes in the workload.
+ *
+ * @param session The performance hint session instance to update.
+ * @param hint The hint to send to the session.
+ * @return 0 on success
+ * EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_sendHint(
+ APerformanceHintSession* session, int hint) __INTRODUCED_IN(__ANDROID_API_U__);
+
__END_DECLS
#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 105f952..085fc27 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -601,12 +601,15 @@
float accuracy;
} AHeadingEvent;
+// LINT.IfChange
/**
* Information that describes a sensor event, refer to
* <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
* documentation.
+ *
+ * NOTE: changes to this struct has to be backward compatible and reflected in
+ * sensors_event_t
*/
-/* NOTE: changes to this struct has to be backward compatible */
typedef struct ASensorEvent {
int32_t version; /* sizeof(struct ASensorEvent) */
int32_t sensor; /** The sensor that generates this event */
@@ -651,6 +654,7 @@
uint32_t flags;
int32_t reserved1[3];
} ASensorEvent;
+// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
struct ASensorManager;
/**
diff --git a/include/android/surface_control_jni.h b/include/android/surface_control_jni.h
index 67e3a9f..840f6e7 100644
--- a/include/android/surface_control_jni.h
+++ b/include/android/surface_control_jni.h
@@ -36,14 +36,15 @@
/**
* Return the ASurfaceControl wrapped by a Java SurfaceControl object.
*
- * This method does not acquire any additional reference to the ASurfaceControl
- * that is returned. To keep the ASurfaceControl alive after the Java
- * SurfaceControl object is closed, explicitly or by the garbage collector, be
- * sure to use ASurfaceControl_acquire() to acquire an additional reference.
+ * The caller takes ownership of the returned ASurfaceControl returned and must
+ * release it * using ASurfaceControl_release.
+ *
+ * surfaceControlObj must be a non-null instance of android.view.SurfaceControl
+ * and isValid() must be true.
*
* Available since API level 34.
*/
-ASurfaceControl* _Nullable ASurfaceControl_fromSurfaceControl(JNIEnv* _Nonnull env,
+ASurfaceControl* _Nonnull ASurfaceControl_fromJava(JNIEnv* _Nonnull env,
jobject _Nonnull surfaceControlObj) __INTRODUCED_IN(__ANDROID_API_U__);
/**
@@ -52,11 +53,13 @@
* The returned ASurfaceTransaction is still owned by the Java Transaction object is only
* valid while the Java Transaction object is alive. In particular, the returned transaction
* must NOT be deleted with ASurfaceTransaction_delete.
- * May return nullptr on error.
+ *
+ * transactionObj must be a non-null instance of
+ * android.view.SurfaceControl.Transaction and close() must not already be called.
*
* Available since API level 34.
*/
-ASurfaceTransaction* _Nullable ASurfaceTransaction_fromTransaction(JNIEnv* _Nonnull env,
+ASurfaceTransaction* _Nonnull ASurfaceTransaction_fromJava(JNIEnv* _Nonnull env,
jobject _Nonnull transactionObj) __INTRODUCED_IN(__ANDROID_API_U__);
__END_DECLS
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
index b0bbb95..94484ea 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -87,13 +87,14 @@
__BEGIN_DECLS
+struct ASystemFontIterator;
/**
* ASystemFontIterator provides access to the system font configuration.
*
* ASystemFontIterator is an iterator for all available system font settings.
* This iterator is not a thread-safe object. Do not pass this iterator to other threads.
*/
-struct ASystemFontIterator;
+typedef struct ASystemFontIterator ASystemFontIterator;
/**
* Create a system font iterator.
diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h
index 571a361..d725be1 100644
--- a/include/attestation/HmacKeyManager.h
+++ b/include/attestation/HmacKeyManager.h
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#ifndef ATTESTATION_HMACKEYMANAGER_H
+#define ATTESTATION_HMACKEYMANAGER_H
+
#include <array>
namespace android {
@@ -29,4 +32,6 @@
private:
const std::array<uint8_t, 128> mHmacKey;
};
-} // namespace android
\ No newline at end of file
+} // namespace android
+
+#endif // ATTESTATION_HMACKEYMANAGER_H
\ No newline at end of file
diff --git a/include/ftl/concat.h b/include/ftl/concat.h
index ded48f7..e0774d3 100644
--- a/include/ftl/concat.h
+++ b/include/ftl/concat.h
@@ -20,7 +20,9 @@
namespace android::ftl {
-// Lightweight (not allocating nor sprintf-based) concatenation.
+// Lightweight (not allocating nor sprintf-based) concatenation. The variadic arguments can be
+// values of integral type (including bool and char), string literals, or strings whose length
+// is constrained:
//
// std::string_view name = "Volume";
// ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB");
diff --git a/include/ftl/details/concat.h b/include/ftl/details/concat.h
index 8ce949e..726ba02 100644
--- a/include/ftl/details/concat.h
+++ b/include/ftl/details/concat.h
@@ -19,6 +19,7 @@
#include <functional>
#include <string_view>
+#include <ftl/details/type_traits.h>
#include <ftl/string.h>
namespace android::ftl::details {
@@ -26,16 +27,42 @@
template <typename T, typename = void>
struct StaticString;
+// Booleans.
template <typename T>
-struct StaticString<T, std::enable_if_t<std::is_integral_v<T>>> {
- static constexpr std::size_t N = to_chars_length_v<T>;
+struct StaticString<T, std::enable_if_t<is_bool_v<T>>> {
+ static constexpr std::size_t N = 5; // Length of "false".
- explicit StaticString(T v) : view(to_chars(buffer, v)) {}
+ explicit constexpr StaticString(bool b) : view(b ? "true" : "false") {}
- to_chars_buffer_t<T> buffer;
const std::string_view view;
};
+// Characters.
+template <typename T>
+struct StaticString<T, std::enable_if_t<is_char_v<T>>> {
+ static constexpr std::size_t N = 1;
+
+ explicit constexpr StaticString(char c) : character(c) {}
+
+ const char character;
+ const std::string_view view{&character, 1u};
+};
+
+// Integers, including the integer value of other character types like char32_t.
+template <typename T>
+struct StaticString<
+ T, std::enable_if_t<std::is_integral_v<remove_cvref_t<T>> && !is_bool_v<T> && !is_char_v<T>>> {
+ using U = remove_cvref_t<T>;
+ static constexpr std::size_t N = to_chars_length_v<U>;
+
+ // TODO: Mark this and to_chars as `constexpr` in C++23.
+ explicit StaticString(U v) : view(to_chars(buffer, v)) {}
+
+ to_chars_buffer_t<U> buffer;
+ const std::string_view view;
+};
+
+// Character arrays.
template <std::size_t M>
struct StaticString<const char (&)[M], void> {
static constexpr std::size_t N = M - 1;
@@ -50,6 +77,7 @@
std::string_view view;
};
+// Strings with constrained length.
template <std::size_t M>
struct StaticString<Truncated<M>, void> {
static constexpr std::size_t N = M;
diff --git a/include/ftl/details/match.h b/include/ftl/details/match.h
new file mode 100644
index 0000000..51b99d2
--- /dev/null
+++ b/include/ftl/details/match.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <type_traits>
+#include <variant>
+
+namespace android::ftl::details {
+
+template <typename... Ms>
+struct Matcher : Ms... {
+ using Ms::operator()...;
+};
+
+// Deduction guide.
+template <typename... Ms>
+Matcher(Ms...) -> Matcher<Ms...>;
+
+template <typename Matcher, typename... Ts>
+constexpr bool is_exhaustive_match_v = (std::is_invocable_v<Matcher, Ts> && ...);
+
+template <typename...>
+struct Match;
+
+template <typename T, typename U, typename... Ts>
+struct Match<T, U, Ts...> {
+ template <typename Variant, typename Matcher>
+ static decltype(auto) match(Variant& variant, const Matcher& matcher) {
+ if (auto* const ptr = std::get_if<T>(&variant)) {
+ return matcher(*ptr);
+ } else {
+ return Match<U, Ts...>::match(variant, matcher);
+ }
+ }
+};
+
+template <typename T>
+struct Match<T> {
+ template <typename Variant, typename Matcher>
+ static decltype(auto) match(Variant& variant, const Matcher& matcher) {
+ return matcher(std::get<T>(variant));
+ }
+};
+
+} // namespace android::ftl::details
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/include/ftl/details/mixins.h
similarity index 62%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to include/ftl/details/mixins.h
index 9fac3e8..9ab9e08 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/include/ftl/details/mixins.h
@@ -14,10 +14,17 @@
* limitations under the License.
*/
-package android.gui;
+#pragma once
-/** @hide */
-parcelable MirrorSurfaceResult {
- IBinder handle;
- int layerId;
-}
+namespace android::ftl::details {
+
+template <typename Self, template <typename> class>
+class Mixin {
+ protected:
+ constexpr Self& self() { return *static_cast<Self*>(this); }
+ constexpr const Self& self() const { return *static_cast<const Self*>(this); }
+
+ constexpr auto& mut() { return self().value_; }
+};
+
+} // namespace android::ftl::details
diff --git a/include/ftl/details/type_traits.h b/include/ftl/details/type_traits.h
index 7092ec5..47bebc5 100644
--- a/include/ftl/details/type_traits.h
+++ b/include/ftl/details/type_traits.h
@@ -24,4 +24,10 @@
template <typename U>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+template <typename T>
+constexpr bool is_bool_v = std::is_same_v<remove_cvref_t<T>, bool>;
+
+template <typename T>
+constexpr bool is_char_v = std::is_same_v<remove_cvref_t<T>, char>;
+
} // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index 82af1d6..075d12b 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -92,7 +92,7 @@
// enum class E { A, B, C };
// static_assert(ftl::to_underlying(E::B) == 1);
//
-template <typename E>
+template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
constexpr auto to_underlying(E v) {
return static_cast<std::underlying_type_t<E>>(v);
}
diff --git a/include/ftl/flags.h b/include/ftl/flags.h
index 70aaa0e..cdb4e84 100644
--- a/include/ftl/flags.h
+++ b/include/ftl/flags.h
@@ -125,7 +125,7 @@
/* Tests whether all of the given flags are set */
bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; }
- Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
+ constexpr Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); }
Flags<F>& operator|=(Flags<F> rhs) {
mFlags = mFlags | rhs.mFlags;
return *this;
@@ -217,7 +217,7 @@
}
template <typename F, typename = std::enable_if_t<is_scoped_enum_v<F>>>
-Flags<F> operator|(F lhs, F rhs) {
+constexpr Flags<F> operator|(F lhs, F rhs) {
return static_cast<F>(to_underlying(lhs) | to_underlying(rhs));
}
diff --git a/include/ftl/match.h b/include/ftl/match.h
new file mode 100644
index 0000000..7318c45
--- /dev/null
+++ b/include/ftl/match.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <utility>
+#include <variant>
+
+#include <ftl/details/match.h>
+
+namespace android::ftl {
+
+// Concise alternative to std::visit that compiles to branches rather than a dispatch table. For
+// std::variant<T0, ..., TN> where N is small, this is slightly faster since the branches can be
+// inlined unlike the function pointers.
+//
+// using namespace std::chrono;
+// std::variant<seconds, minutes, hours> duration = 119min;
+//
+// // Mutable match.
+// ftl::match(duration, [](auto& d) { ++d; });
+//
+// // Immutable match. Exhaustive due to minutes being convertible to seconds.
+// assert("2 hours"s ==
+// ftl::match(duration,
+// [](const seconds& s) {
+// const auto h = duration_cast<hours>(s);
+// return std::to_string(h.count()) + " hours"s;
+// },
+// [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; }));
+//
+template <typename... Ts, typename... Ms>
+decltype(auto) match(std::variant<Ts...>& variant, Ms&&... matchers) {
+ const auto matcher = details::Matcher{std::forward<Ms>(matchers)...};
+ static_assert(details::is_exhaustive_match_v<decltype(matcher), Ts&...>, "Non-exhaustive match");
+
+ return details::Match<Ts...>::match(variant, matcher);
+}
+
+template <typename... Ts, typename... Ms>
+decltype(auto) match(const std::variant<Ts...>& variant, Ms&&... matchers) {
+ const auto matcher = details::Matcher{std::forward<Ms>(matchers)...};
+ static_assert(details::is_exhaustive_match_v<decltype(matcher), const Ts&...>,
+ "Non-exhaustive match");
+
+ return details::Match<Ts...>::match(variant, matcher);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/mixins.h b/include/ftl/mixins.h
new file mode 100644
index 0000000..0e1d200
--- /dev/null
+++ b/include/ftl/mixins.h
@@ -0,0 +1,148 @@
+/*
+ * 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 <ftl/details/mixins.h>
+
+namespace android::ftl {
+
+// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common
+// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must
+// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic,
+// etc.) are enabled through inheritance:
+//
+// struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
+// using Constructible::Constructible;
+// };
+//
+// static_assert(!std::is_default_constructible_v<Id>);
+//
+// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is
+// zero-initialized unless specified:
+//
+// struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
+// ftl::Equatable<Color>,
+// ftl::Orderable<Color> {
+// using DefaultConstructible::DefaultConstructible;
+// };
+//
+// static_assert(Color() == Color(0u));
+// static_assert(ftl::to_underlying(Color(-1)) == 255u);
+// static_assert(Color(1u) < Color(2u));
+//
+// struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
+// ftl::Equatable<Sequence>,
+// ftl::Orderable<Sequence>,
+// ftl::Incrementable<Sequence> {
+// using DefaultConstructible::DefaultConstructible;
+// };
+//
+// static_assert(Sequence() == Sequence(-1));
+//
+// The underlying type need not be a fundamental type:
+//
+// struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
+// ftl::Equatable<Timeout>,
+// ftl::Addable<Timeout> {
+// using DefaultConstructible::DefaultConstructible;
+// };
+//
+// using namespace std::chrono_literals;
+// static_assert(Timeout() + Timeout(5s) == Timeout(15s));
+//
+template <typename Self, typename T>
+struct Constructible {
+ explicit constexpr Constructible(T value) : value_(value) {}
+
+ explicit constexpr operator const T&() const { return value_; }
+
+ private:
+ template <typename, template <typename> class>
+ friend class details::Mixin;
+
+ T value_;
+};
+
+template <typename Self, typename T, auto kDefault = T{}>
+struct DefaultConstructible : Constructible<Self, T> {
+ using Constructible<Self, T>::Constructible;
+ constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {}
+};
+
+// Shorthand for casting a type-safe wrapper to its underlying value.
+template <typename Self, typename T>
+constexpr const T& to_underlying(const Constructible<Self, T>& c) {
+ return static_cast<const T&>(c);
+}
+
+// Comparison operators for equality.
+template <typename Self>
+struct Equatable : details::Mixin<Self, Equatable> {
+ constexpr bool operator==(const Self& other) const {
+ return to_underlying(this->self()) == to_underlying(other);
+ }
+
+ constexpr bool operator!=(const Self& other) const { return !(*this == other); }
+};
+
+// Comparison operators for ordering.
+template <typename Self>
+struct Orderable : details::Mixin<Self, Orderable> {
+ constexpr bool operator<(const Self& other) const {
+ return to_underlying(this->self()) < to_underlying(other);
+ }
+
+ constexpr bool operator>(const Self& other) const { return other < this->self(); }
+ constexpr bool operator>=(const Self& other) const { return !(*this < other); }
+ constexpr bool operator<=(const Self& other) const { return !(*this > other); }
+};
+
+// Pre-increment and post-increment operators.
+template <typename Self>
+struct Incrementable : details::Mixin<Self, Incrementable> {
+ constexpr Self& operator++() {
+ ++this->mut();
+ return this->self();
+ }
+
+ constexpr Self operator++(int) {
+ const Self tmp = this->self();
+ operator++();
+ return tmp;
+ }
+};
+
+// Additive operators, including incrementing.
+template <typename Self>
+struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> {
+ constexpr Self& operator+=(const Self& other) {
+ this->mut() += to_underlying(other);
+ return this->self();
+ }
+
+ constexpr Self operator+(const Self& other) const {
+ Self tmp = this->self();
+ return tmp += other;
+ }
+
+ private:
+ using Base = details::Mixin<Self, Addable>;
+ using Base::mut;
+ using Base::self;
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h
new file mode 100644
index 0000000..35d09d7
--- /dev/null
+++ b/include/ftl/non_null.h
@@ -0,0 +1,116 @@
+/*
+ * 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 <cstdlib>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Enforces and documents non-null pre/post-condition for (raw or smart) pointers.
+//
+// void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr,
+// ftl::NonNull<std::size_t*> length_ptr) {
+// // No need for `nullptr` checks.
+// *length_ptr = string_ptr->length();
+// }
+//
+// const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
+// std::size_t size;
+// get_length(string_ptr, ftl::as_non_null(&size));
+// assert(size == 7u);
+//
+// For compatibility with std::unique_ptr<T> and performance with std::shared_ptr<T>, move
+// operations are allowed despite breaking the invariant:
+//
+// using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>;
+//
+// Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) {
+// // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point.
+// auto unique_ptr = std::move(non_null_ptr).take();
+//
+// auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr)));
+// auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr;
+//
+// return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)};
+// }
+//
+// auto ptr = ftl::as_non_null(std::make_unique<int>(42));
+// const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true);
+// assert(ptr1.get() == ptr2);
+//
+template <typename Pointer>
+class NonNull final {
+ struct Passkey {};
+
+ public:
+ // Disallow `nullptr` explicitly for clear compilation errors.
+ NonNull() = delete;
+ NonNull(std::nullptr_t) = delete;
+
+ // Copy operations.
+
+ constexpr NonNull(const NonNull&) = default;
+ constexpr NonNull& operator=(const NonNull&) = default;
+
+ constexpr const Pointer& get() const { return pointer_; }
+ constexpr explicit operator const Pointer&() const { return get(); }
+
+ // Move operations. These break the invariant, so care must be taken to avoid subsequent access.
+
+ constexpr NonNull(NonNull&&) = default;
+ constexpr NonNull& operator=(NonNull&&) = default;
+
+ constexpr Pointer take() && { return std::move(pointer_); }
+ constexpr explicit operator Pointer() && { return take(); }
+
+ // Dereferencing.
+ constexpr decltype(auto) operator*() const { return *get(); }
+ constexpr decltype(auto) operator->() const { return get(); }
+
+ // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions
+ // through the passkey idiom, for clear compilation errors.
+ template <typename P>
+ constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) {
+ if (!pointer_) std::abort();
+ }
+
+ private:
+ template <typename P>
+ friend constexpr auto as_non_null(P&&) -> NonNull<std::decay_t<P>>;
+
+ Pointer pointer_;
+};
+
+template <typename P>
+constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
+ using Passkey = typename NonNull<std::decay_t<P>>::Passkey;
+ return {Passkey{}, std::forward<P>(pointer)};
+}
+
+template <typename P, typename Q>
+constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() == rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index 626507f..a818128 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -95,8 +95,26 @@
if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
return R();
}
+
+ // Delete new for this class. Its base doesn't have a virtual destructor, and
+ // if it got deleted via base class pointer, it would cause undefined
+ // behavior. There's not a good reason to allocate this object on the heap
+ // anyway.
+ static void* operator new(size_t) = delete;
+ static void* operator new[](size_t) = delete;
+
};
+template <typename T, typename U>
+constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
+ return static_cast<std::optional<T>>(lhs) == static_cast<std::optional<U>>(rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
+ return !(lhs == rhs);
+}
+
// Deduction guides.
template <typename T>
Optional(T) -> Optional<T>;
diff --git a/include/ftl/shared_mutex.h b/include/ftl/shared_mutex.h
new file mode 100644
index 0000000..146f5ba
--- /dev/null
+++ b/include/ftl/shared_mutex.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <shared_mutex>
+
+namespace android::ftl {
+
+// Wrapper around std::shared_mutex to provide capabilities for thread-safety
+// annotations.
+// TODO(b/257958323): This class is no longer needed once b/135688034 is fixed (currently blocked on
+// b/175635923).
+class [[clang::capability("shared_mutex")]] SharedMutex final {
+ public:
+ [[clang::acquire_capability()]] void lock() {
+ mutex_.lock();
+ }
+ [[clang::release_capability()]] void unlock() {
+ mutex_.unlock();
+ }
+
+ [[clang::acquire_shared_capability()]] void lock_shared() {
+ mutex_.lock_shared();
+ }
+ [[clang::release_shared_capability()]] void unlock_shared() {
+ mutex_.unlock_shared();
+ }
+
+ private:
+ std::shared_mutex mutex_;
+};
+
+} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 98a18c9..7457496 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -21,6 +21,7 @@
#include <ftl/string.h>
#include <gui/constants.h>
#include <input/Input.h>
+#include <ui/Rotation.h>
#include <cinttypes>
#include <optional>
@@ -29,13 +30,6 @@
namespace android {
-enum {
- DISPLAY_ORIENTATION_0 = 0,
- DISPLAY_ORIENTATION_90 = 1,
- DISPLAY_ORIENTATION_180 = 2,
- DISPLAY_ORIENTATION_270 = 3
-};
-
/**
* Describes the different type of viewports supported by input flinger.
* Keep in sync with values in InputManagerService.java.
@@ -54,7 +48,7 @@
*/
struct DisplayViewport {
int32_t displayId; // -1 if invalid
- int32_t orientation;
+ ui::Rotation orientation;
int32_t logicalLeft;
int32_t logicalTop;
int32_t logicalRight;
@@ -74,7 +68,7 @@
DisplayViewport()
: displayId(ADISPLAY_ID_NONE),
- orientation(DISPLAY_ORIENTATION_0),
+ orientation(ui::ROTATION_0),
logicalLeft(0),
logicalTop(0),
logicalRight(0),
@@ -111,7 +105,7 @@
void setNonDisplayViewport(int32_t width, int32_t height) {
displayId = ADISPLAY_ID_NONE;
- orientation = DISPLAY_ORIENTATION_0;
+ orientation = ui::ROTATION_0;
logicalLeft = 0;
logicalTop = 0;
logicalRight = width;
diff --git a/include/input/Input.h b/include/input/Input.h
index 172e5b4..015efdd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -209,6 +209,8 @@
bool isFromSource(uint32_t source, uint32_t test);
+bool isStylusToolType(uint32_t toolType);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -366,7 +368,7 @@
// Values of axes that are stored in this structure packed in order by axis id
// for each axis that is present in the structure according to 'bits'.
- float values[MAX_AXES];
+ std::array<float, MAX_AXES> values;
inline void clear() {
BitSet64::clear(bits);
@@ -406,7 +408,8 @@
return !(*this == other);
}
- void copyFrom(const PointerCoords& other);
+ inline void copyFrom(const PointerCoords& other) { *this = other; }
+ PointerCoords& operator=(const PointerCoords&) = default;
private:
void tooManyAxes(int axis);
@@ -574,7 +577,7 @@
inline const ui::Transform& getTransform() const { return mTransform; }
- int getSurfaceRotation() const;
+ std::optional<ui::Rotation> getSurfaceRotation() const;
inline float getXPrecision() const { return mXPrecision; }
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 0026e82..e911734 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -23,6 +23,7 @@
#include <unordered_map>
#include <vector>
+#include <android/os/IInputConstants.h>
#include "android/hardware/input/InputDeviceCountryCode.h"
namespace android {
@@ -57,6 +58,9 @@
// reuse values that are not associated with an input anymore.
uint16_t nonce;
+ // The bluetooth address of the device, if known.
+ std::optional<std::string> bluetoothAddress;
+
/**
* Return InputDeviceIdentifier.name that has been adjusted as follows:
* - all characters besides alphanumerics, dash,
@@ -280,6 +284,9 @@
std::vector<InputDeviceLightInfo> getLights();
+ inline void setSupportsUsi(bool supportsUsi) { mSupportsUsi = supportsUsi; }
+ inline bool supportsUsi() const { return mSupportsUsi; }
+
private:
int32_t mId;
int32_t mGeneration;
@@ -292,6 +299,8 @@
uint32_t mSources;
int32_t mKeyboardType;
std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
+ // Whether this device supports the Universal Stylus Initiative (USI) protocol for styluses.
+ bool mSupportsUsi;
bool mHasVibrator;
bool mHasBattery;
@@ -341,6 +350,8 @@
const std::string& name, InputDeviceConfigurationFileType type);
enum ReservedInputDeviceId : int32_t {
+ // Device id representing an invalid device
+ INVALID_INPUT_DEVICE_ID = android::os::IInputConstants::INVALID_INPUT_DEVICE_ID,
// Device id of a special "virtual" keyboard that is always present.
VIRTUAL_KEYBOARD_ID = -1,
// Device id of the "built-in" keyboard if there is one.
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dc928b8..867a089 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -125,14 +125,21 @@
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
Vector<KeyEvent>& outEvents) const;
+ /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
+ * and usageCodes are mapped to corresponding Android Keycode */
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
/* Maps a scan code and usage code to a key code, in case this key map overrides
* the mapping in some way. */
status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
- /* Tries to find a replacement key code for a given key code and meta state
- * in character map. */
- void tryRemapKey(int32_t scanCode, int32_t metaState,
- int32_t* outKeyCode, int32_t* outMetaState) const;
+ /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
+ int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+
+ /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
+ * that tries to find a replacement key code based on current meta state */
+ std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
+ int32_t metaState) const;
#ifdef __linux__
/* Reads a key map from a parcel. */
@@ -227,8 +234,9 @@
std::string mLoadFileName;
bool mLayoutOverlayApplied;
- KeyedVector<int32_t, int32_t> mKeysByScanCode;
- KeyedVector<int32_t, int32_t> mKeysByUsageCode;
+ std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
+ std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
+ std::map<int32_t /* fromHidUsageCode */, int32_t /* toAndroidKeyCode */> mKeysByUsageCode;
KeyCharacterMap(const std::string& filename);
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 55f730b..e24344b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -24,16 +24,20 @@
namespace android {
template <typename T>
-std::string constToString(const T& v) {
+inline std::string constToString(const T& v) {
return std::to_string(v);
}
+inline std::string constToString(const std::string& s) {
+ return s;
+}
+
/**
* Convert an optional type to string.
*/
template <typename T>
-std::string toString(const std::optional<T>& optional,
- std::string (*toString)(const T&) = constToString) {
+inline std::string toString(const std::optional<T>& optional,
+ std::string (*toString)(const T&) = constToString) {
return optional ? toString(*optional) : "<not set>";
}
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index a616a95..1e4f6e7 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ui/Rotation.h>
+
#include <stdint.h>
#include <sys/time.h>
#include <vector>
@@ -58,7 +60,7 @@
* Rotate the video frame.
* The rotation value is an enum from ui/Rotation.h
*/
- void rotate(int32_t orientation);
+ void rotate(ui::Rotation orientation);
private:
uint32_t mHeight;
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index f72a1bd..f3c201e 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -16,10 +16,13 @@
#pragma once
+#include <android-base/stringprintf.h>
#include <input/Input.h>
#include <input/VelocityTracker.h>
#include <utils/Timers.h>
+using android::base::StringPrintf;
+
namespace android {
/*
@@ -69,6 +72,12 @@
scale(scale), lowThreshold(lowThreshold),
highThreshold(highThreshold), acceleration(acceleration) {
}
+
+ std::string dump() const {
+ return StringPrintf("scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, "
+ "acceleration=%0.3f\n",
+ scale, lowThreshold, highThreshold, acceleration);
+ }
};
/*
@@ -78,6 +87,9 @@
public:
VelocityControl();
+ /* Gets the various parameters. */
+ VelocityControlParameters& getParameters();
+
/* Sets the various parameters. */
void setParameters(const VelocityControlParameters& parameters);
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 294879e..62c3ae1 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -106,6 +106,9 @@
~VelocityTracker();
+ /** Return true if the axis is supported for velocity tracking, false otherwise. */
+ static bool isAxisSupported(int32_t axis);
+
// Resets the velocity tracker state.
void clear();
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index ed6f6f3..e0384f3 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -19,6 +19,8 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
namespace android {
@@ -32,12 +34,16 @@
static sp<hardware::power::IPower> loadAidl();
static sp<hardware::power::V1_0::IPower> loadHidlV1_0();
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
+ static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
+ static sp<hardware::power::V1_3::IPower> loadHidlV1_3();
private:
static std::mutex gHalMutex;
static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_2::IPower> gHalHidlV1_2 GUARDED_BY(gHalMutex);
+ static sp<hardware::power::V1_3::IPower> gHalHidlV1_3 GUARDED_BY(gHalMutex);
static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index dfb0ff5..8028aa8 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -19,6 +19,8 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/Boost.h>
#include <android/hardware/power/IPower.h>
#include <android/hardware/power/IPowerHintSession.h>
@@ -142,8 +144,8 @@
// Wrapper for the HIDL Power HAL v1.0.
class HidlHalWrapperV1_0 : public HalWrapper {
public:
- explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal)
- : mHandleV1_0(std::move(Hal)) {}
+ explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> handleV1_0)
+ : mHandleV1_0(std::move(handleV1_0)) {}
virtual ~HidlHalWrapperV1_0() = default;
virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
@@ -154,10 +156,10 @@
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data);
+ const sp<hardware::power::V1_0::IPower> mHandleV1_0;
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId, uint32_t data);
private:
- sp<hardware::power::V1_0::IPower> mHandleV1_0;
HalResult<void> setInteractive(bool enabled);
HalResult<void> setFeature(hardware::power::V1_0::Feature feature, bool enabled);
};
@@ -165,17 +167,40 @@
// Wrapper for the HIDL Power HAL v1.1.
class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 {
public:
- HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0,
- sp<hardware::power::V1_1::IPower> handleV1_1)
- : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {}
+ HidlHalWrapperV1_1(sp<hardware::power::V1_1::IPower> handleV1_1)
+ : HidlHalWrapperV1_0(std::move(handleV1_1)) {}
virtual ~HidlHalWrapperV1_1() = default;
protected:
- virtual HalResult<void> sendPowerHint(hardware::power::V1_0::PowerHint hintId,
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
uint32_t data) override;
+};
-private:
- sp<hardware::power::V1_1::IPower> mHandleV1_1;
+// Wrapper for the HIDL Power HAL v1.2.
+class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 {
+public:
+ virtual HalResult<void> setBoost(hardware::power::Boost boost, int32_t durationMs) override;
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2)
+ : HidlHalWrapperV1_1(std::move(handleV1_2)) {}
+ virtual ~HidlHalWrapperV1_2() = default;
+
+protected:
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
+ uint32_t data) override;
+};
+
+// Wrapper for the HIDL Power HAL v1.3.
+class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 {
+public:
+ virtual HalResult<void> setMode(hardware::power::Mode mode, bool enabled) override;
+ HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3)
+ : HidlHalWrapperV1_2(std::move(handleV1_3)) {}
+ virtual ~HidlHalWrapperV1_3() = default;
+
+protected:
+ virtual HalResult<void> sendPowerHint(hardware::power::V1_3::PowerHint hintId,
+ uint32_t data) override;
};
// Wrapper for the AIDL Power HAL.
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 15bd5c3..ebc74fb 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -23,8 +23,10 @@
#include <sys/eventfd.h>
#include <sys/uio.h>
+#include <atomic>
#include <chrono>
#include <deque>
+#include <optional>
#include <string>
#include <string_view>
#include <tuple>
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 76e3e66..5e539f2 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -49,6 +49,9 @@
"com.android.media",
"com.android.media.swcodec",
],
+ llndk: {
+ llndk_headers: true,
+ },
}
cc_library_static {
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index 2bf15d4..fddecc0 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -22,6 +22,7 @@
cc_library_static {
name: "libattestation",
+ host_supported: true,
cflags: [
"-Wall",
"-Wextra",
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index c4bb6d0..f17bb7d 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -161,7 +161,7 @@
],
header_libs: [
- "libandroid_runtime_vm_headers",
+ "jni_headers",
],
export_header_lib_headers: [
@@ -315,7 +315,7 @@
},
recovery: {
exclude_header_libs: [
- "libandroid_runtime_vm_headers",
+ "jni_headers",
],
},
},
@@ -484,9 +484,6 @@
java: {
enabled: false,
},
- cpp: {
- gen_trace: false,
- },
},
}
@@ -498,6 +495,7 @@
"libbase",
"libbinder",
"libbinder_ndk",
+ "libcutils_sockets",
"liblog",
"libutils",
],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 481d704..5e725a9 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -232,7 +232,10 @@
: mRpcServer(rpcServer), mKeepAliveBinder(keepAliveBinder), mBinder(binder) {}
virtual ~RpcServerLink();
void binderDied(const wp<IBinder>&) override {
- LOG_RPC_DETAIL("RpcServerLink: binder died, shutting down RpcServer");
+ auto promoted = mBinder.promote();
+ ALOGI("RpcBinder: binder died, shutting down RpcServer for %s",
+ promoted ? String8(promoted->getInterfaceDescriptor()).c_str() : "<NULL>");
+
if (mRpcServer == nullptr) {
ALOGW("RpcServerLink: Unable to shut down RpcServer because it does not exist.");
} else {
@@ -241,11 +244,7 @@
}
mRpcServer.clear();
- auto promoted = mBinder.promote();
- if (promoted == nullptr) {
- ALOGW("RpcServerLink: Unable to remove link from parent binder object because parent "
- "binder object is gone.");
- } else {
+ if (promoted) {
promoted->removeRpcServerLink(sp<RpcServerLink>::fromExisting(this));
}
mBinder.clear();
@@ -706,6 +705,7 @@
return status;
}
rpcServer->setMaxThreads(binderThreadPoolMaxCount);
+ LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor();
rpcServer->start();
e->mRpcServerLinks.emplace(link);
LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 11c8e5d..c0f3e30 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -45,11 +45,11 @@
#define IF_LOG_TRANSACTIONS() if (false)
#define IF_LOG_COMMANDS() if (false)
-#define LOG_REMOTEREFS(...)
+#define LOG_REMOTEREFS(...)
#define IF_LOG_REMOTEREFS() if (false)
-#define LOG_THREADPOOL(...)
-#define LOG_ONEWAY(...)
+#define LOG_THREADPOOL(...)
+#define LOG_ONEWAY(...)
#else
@@ -394,14 +394,92 @@
// context, so we don't abort
}
+constexpr uint32_t encodeExplicitIdentity(bool hasExplicitIdentity, pid_t callingPid) {
+ uint32_t as_unsigned = static_cast<uint32_t>(callingPid);
+ if (hasExplicitIdentity) {
+ return as_unsigned | (1 << 30);
+ } else {
+ return as_unsigned & ~(1 << 30);
+ }
+}
+
+constexpr int64_t packCallingIdentity(bool hasExplicitIdentity, uid_t callingUid,
+ pid_t callingPid) {
+ // Calling PID is a 32-bit signed integer, but doesn't consume the entire 32 bit space.
+ // To future-proof this and because we have extra capacity, we decided to also support -1,
+ // since this constant is used to represent invalid UID in other places of the system.
+ // Thus, we pack hasExplicitIdentity into the 2nd bit from the left. This allows us to
+ // preserve the (left-most) bit for the sign while also encoding the value of
+ // hasExplicitIdentity.
+ // 32b | 1b | 1b | 30b
+ // token = [ calling uid | calling pid(sign) | has explicit identity | calling pid(rest) ]
+ uint64_t token = (static_cast<uint64_t>(callingUid) << 32) |
+ encodeExplicitIdentity(hasExplicitIdentity, callingPid);
+ return static_cast<int64_t>(token);
+}
+
+constexpr bool unpackHasExplicitIdentity(int64_t token) {
+ return static_cast<int32_t>(token) & (1 << 30);
+}
+
+constexpr uid_t unpackCallingUid(int64_t token) {
+ return static_cast<uid_t>(token >> 32);
+}
+
+constexpr pid_t unpackCallingPid(int64_t token) {
+ int32_t encodedPid = static_cast<int32_t>(token);
+ if (encodedPid & (1 << 31)) {
+ return encodedPid | (1 << 30);
+ } else {
+ return encodedPid & ~(1 << 30);
+ }
+}
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, 9999)) == true,
+ "pack true hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(true, 1000, 9999)) == 1000, "pack true uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(true, 1000, 9999)) == 9999, "pack true pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, 9999)) == false,
+ "pack false hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(false, 1000, 9999)) == 1000, "pack false uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(false, 1000, 9999)) == 9999, "pack false pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, -1)) == true,
+ "pack true (negative) hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(true, 1000, -1)) == 1000,
+ "pack true (negative) uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(true, 1000, -1)) == -1,
+ "pack true (negative) pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, -1)) == false,
+ "pack false (negative) hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(false, 1000, -1)) == 1000,
+ "pack false (negative) uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(false, 1000, -1)) == -1,
+ "pack false (negative) pid");
+
int64_t IPCThreadState::clearCallingIdentity()
{
// ignore mCallingSid for legacy reasons
- int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
+ int64_t token = packCallingIdentity(mHasExplicitIdentity, mCallingUid, mCallingPid);
clearCaller();
+ mHasExplicitIdentity = true;
return token;
}
+bool IPCThreadState::hasExplicitIdentity() {
+ return mHasExplicitIdentity;
+}
+
void IPCThreadState::setStrictModePolicy(int32_t policy)
{
mStrictModePolicy = policy;
@@ -474,9 +552,10 @@
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
- mCallingUid = (int)(token>>32);
+ mCallingUid = unpackCallingUid(token);
mCallingSid = nullptr; // not enough data to restore
- mCallingPid = (int)token;
+ mCallingPid = unpackCallingPid(token);
+ mHasExplicitIdentity = unpackHasExplicitIdentity(token);
}
void IPCThreadState::clearCaller()
@@ -889,6 +968,7 @@
mCallRestriction(mProcess->mCallRestriction) {
pthread_setspecific(gTLS, this);
clearCaller();
+ mHasExplicitIdentity = false;
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
@@ -937,6 +1017,10 @@
if (!reply && !acquireResult) goto finish;
break;
+ case BR_TRANSACTION_PENDING_FROZEN:
+ ALOGW("Sending oneway calls to frozen process.");
+ goto finish;
+
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
@@ -1279,6 +1363,7 @@
const pid_t origPid = mCallingPid;
const char* origSid = mCallingSid;
const uid_t origUid = mCallingUid;
+ const bool origHasExplicitIdentity = mHasExplicitIdentity;
const int32_t origStrictModePolicy = mStrictModePolicy;
const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
const int32_t origWorkSource = mWorkSource;
@@ -1292,6 +1377,7 @@
mCallingPid = tr.sender_pid;
mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
mCallingUid = tr.sender_euid;
+ mHasExplicitIdentity = false;
mLastTransactionBinderFlags = tr.flags;
// ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
@@ -1367,6 +1453,7 @@
mCallingPid = origPid;
mCallingSid = origSid;
mCallingUid = origUid;
+ mHasExplicitIdentity = origHasExplicitIdentity;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
mWorkSource = origWorkSource;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 05db774..2408307 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -81,6 +81,7 @@
bool isDeclared(const String16& name) override;
Vector<String16> getDeclaredInstances(const String16& interface) override;
std::optional<String16> updatableViaApex(const String16& name) override;
+ Vector<String16> getUpdatableNames(const String16& apexName) override;
std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override;
class RegistrationWaiter : public android::os::BnServiceCallback {
public:
@@ -443,7 +444,7 @@
bool declared;
if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
!status.isOk()) {
- ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+ ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(),
status.toString8().c_str());
return false;
}
@@ -479,6 +480,23 @@
return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
}
+Vector<String16> ServiceManagerShim::getUpdatableNames(const String16& apexName) {
+ std::vector<std::string> out;
+ if (Status status = mTheRealServiceManager->getUpdatableNames(String8(apexName).c_str(), &out);
+ !status.isOk()) {
+ ALOGW("Failed to getUpdatableNames for %s: %s", String8(apexName).c_str(),
+ status.toString8().c_str());
+ return {};
+ }
+
+ Vector<String16> res;
+ res.setCapacity(out.size());
+ for (const std::string& instance : out) {
+ res.push(String16(instance.c_str()));
+ }
+ return res;
+}
+
std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnectionInfo(
const String16& name) {
std::optional<os::ConnectionInfo> connectionInfo;
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp
index 24ce2bb..ce60e33 100644
--- a/libs/binder/OS.cpp
+++ b/libs/binder/OS.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <binder/RpcTransportRaw.h>
+#include <log/log.h>
#include <string.h>
using android::base::ErrnoError;
@@ -25,6 +26,9 @@
namespace android {
+// 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) {
int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
if (flags == -1) {
@@ -63,4 +67,99 @@
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) {
+ if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+ if (ancillaryFds->size() > kMaxFdsPerMsg) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
+ // use memcpy.
+ int fds[kMaxFdsPerMsg];
+ for (size_t i = 0; i < ancillaryFds->size(); i++) {
+ fds[i] = std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+ }
+ const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
+
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
+ memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
+
+ msg.msg_controllen = CMSG_SPACE(fdsByteSize);
+ return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
+ }
+
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ 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) {
+ if (ancillaryFds != nullptr) {
+ int fdBuffer[kMaxFdsPerMsg];
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+ ssize_t processSize = TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+ if (processSize < 0) {
+ return -1;
+ }
+
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
+ // application devs to memcpy the data to ensure memory alignment.
+ size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
+ LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // validity check
+ memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
+ 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]));
+ }
+ break;
+ }
+ }
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ errno = EPIPE;
+ return -1;
+ }
+ return processSize;
+ }
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+
+ return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
} // namespace android
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 5ab8bab..fecae31 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -33,4 +33,12 @@
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 receiveMessageFromSocket(
+ const RpcTransportFd& socket, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds);
+
} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 8333298..ee081c4 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -614,11 +614,14 @@
if (status_t status = readInt32(&fdIndex); status != OK) {
return status;
}
- const auto& oldFd = otherRpcFields->mFds->at(fdIndex);
+ int oldFd = toRawFd(otherRpcFields->mFds->at(fdIndex));
// To match kernel binder behavior, we always dup, even if the
// FD was unowned in the source parcel.
- rpcFields->mFds->emplace_back(
- base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0)));
+ int newFd = -1;
+ if (status_t status = dupFileDescriptor(oldFd, &newFd); status != OK) {
+ ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status));
+ }
+ rpcFields->mFds->emplace_back(base::unique_fd(newFd));
// Fixup the index in the data.
mDataPos = newDataPos + 4;
if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) {
@@ -966,7 +969,15 @@
}
}
+void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) {
+ mEnforceNoDataAvail = enforceNoDataAvail;
+}
+
binder::Status Parcel::enforceNoDataAvail() const {
+ if (!mEnforceNoDataAvail) {
+ return binder::Status::ok();
+ }
+
const auto n = dataAvail();
if (n == 0) {
return binder::Status::ok();
@@ -1439,7 +1450,8 @@
case RpcSession::FileDescriptorTransportMode::NONE: {
return FDS_NOT_ALLOWED;
}
- case RpcSession::FileDescriptorTransportMode::UNIX: {
+ case RpcSession::FileDescriptorTransportMode::UNIX:
+ case RpcSession::FileDescriptorTransportMode::TRUSTY: {
if (rpcFields->mFds == nullptr) {
rpcFields->mFds = std::make_unique<decltype(rpcFields->mFds)::element_type>();
}
@@ -3076,6 +3088,7 @@
mAllowFds = true;
mDeallocZero = false;
mOwner = nullptr;
+ mEnforceNoDataAvail = true;
}
void Parcel::scanForFds() const {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1f311ac..254dda8 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -439,6 +439,10 @@
return mCurrentThreads;
}
+bool ProcessState::isThreadPoolStarted() const {
+ return mThreadPoolStarted;
+}
+
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
static const char* const names[] = {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index e581d0b..0820cd1 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -37,6 +37,7 @@
#include "OS.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
+#include "RpcTransportUtils.h"
#include "RpcWireFormat.h"
#include "Utils.h"
@@ -61,6 +62,10 @@
return sp<RpcServer>::make(std::move(ctx));
}
+status_t RpcServer::setupUnixDomainSocketBootstrapServer(unique_fd bootstrapFd) {
+ return setupExternalServer(std::move(bootstrapFd), &RpcServer::recvmsgSocketConnection);
+}
+
status_t RpcServer::setupUnixDomainServer(const char* path) {
return setupSocketServer(UnixSocketAddress(path));
}
@@ -177,11 +182,50 @@
rpcJoinIfSingleThreaded(*mJoinThread);
}
+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) {
+ int savedErrno = errno;
+ ALOGE("Could not accept4 socket: %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+
+ *out = std::move(clientSocket);
+ return OK;
+}
+
+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;
+
+ if (receiveMessageFromSocket(server.mServer, &iov, 1, &fds) < 0) {
+ int savedErrno = errno;
+ ALOGE("Failed recvmsg: %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+ if (fds.size() != 1) {
+ ALOGE("Expected exactly one fd from recvmsg, got %zu", fds.size());
+ return -EINVAL;
+ }
+
+ unique_fd fd(std::move(std::get<unique_fd>(fds.back())));
+ if (auto res = setNonBlocking(fd); !res.ok()) {
+ ALOGE("Failed setNonBlocking: %s", res.error().message().c_str());
+ return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code();
+ }
+
+ *out = RpcTransportFd(std::move(fd));
+ return OK;
+}
+
void RpcServer::join() {
{
RpcMutexLockGuard _l(mLock);
LOG_ALWAYS_FATAL_IF(!mServer.fd.ok(), "RpcServer must be setup to join.");
+ LOG_ALWAYS_FATAL_IF(mAcceptFn == nullptr, "RpcServer must have an accept() function");
LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
mJoinThreadRunning = true;
mShutdownTrigger = FdTrigger::make();
@@ -192,20 +236,19 @@
while ((status = mShutdownTrigger->triggerablePoll(mServer, POLLIN)) == OK) {
std::array<uint8_t, kRpcAddressSize> addr;
static_assert(addr.size() >= sizeof(sockaddr_storage), "kRpcAddressSize is too small");
-
socklen_t addrLen = addr.size();
- RpcTransportFd clientSocket(unique_fd(TEMP_FAILURE_RETRY(
- accept4(mServer.fd.get(), reinterpret_cast<sockaddr*>(addr.data()), &addrLen,
- SOCK_CLOEXEC | SOCK_NONBLOCK))));
- LOG_ALWAYS_FATAL_IF(addrLen > static_cast<socklen_t>(sizeof(sockaddr_storage)),
- "Truncated address");
-
- if (clientSocket.fd < 0) {
- ALOGE("Could not accept4 socket: %s", strerror(errno));
+ RpcTransportFd clientSocket;
+ if (mAcceptFn(*this, &clientSocket) != OK) {
continue;
}
- LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+ if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
+ &addrLen)) {
+ ALOGE("Could not getpeername socket: %s", strerror(errno));
+ continue;
+ }
+
+ LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
{
RpcMutexLockGuard _l(mLock);
@@ -488,33 +531,35 @@
LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
- RpcTransportFd transportFd(unique_fd(TEMP_FAILURE_RETRY(
- socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))));
- if (!transportFd.fd.ok()) {
+ unique_fd socket_fd(TEMP_FAILURE_RETRY(
+ socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+ if (!socket_fd.ok()) {
int savedErrno = errno;
ALOGE("Could not create socket: %s", strerror(savedErrno));
return -savedErrno;
}
-
- if (0 != TEMP_FAILURE_RETRY(bind(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
+ if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) {
int savedErrno = errno;
ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
return -savedErrno;
}
+ return setupRawSocketServer(std::move(socket_fd));
+}
+
+status_t RpcServer::setupRawSocketServer(unique_fd socket_fd) {
+ LOG_ALWAYS_FATAL_IF(!socket_fd.ok(), "Socket must be setup to listen.");
+
// Right now, we create all threads at once, making accept4 slow. To avoid hanging the client,
// the backlog is increased to a large number.
// TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced
// to 1.
- if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) {
+ if (0 != TEMP_FAILURE_RETRY(listen(socket_fd.get(), 50 /*backlog*/))) {
int savedErrno = errno;
- ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ ALOGE("Could not listen initialized Unix socket: %s", strerror(savedErrno));
return -savedErrno;
}
-
- LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
-
- if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) {
+ if (status_t status = setupExternalServer(std::move(socket_fd)); status != OK) {
ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
return status;
}
@@ -550,16 +595,23 @@
return std::move(mServer.fd);
}
-status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+status_t RpcServer::setupExternalServer(
+ base::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.");
return INVALID_OPERATION;
}
mServer = std::move(serverFd);
+ mAcceptFn = std::move(acceptFn);
return OK;
}
+status_t RpcServer::setupExternalServer(base::unique_fd serverFd) {
+ return setupExternalServer(std::move(serverFd), &RpcServer::acceptSocketConnection);
+}
+
bool RpcServer::hasActiveRequests() {
RpcMutexLockGuard _l(mLock);
for (const auto& [_, session] : mSessions) {
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 49843e5..ce6ef2b 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -41,12 +41,13 @@
#include "OS.h"
#include "RpcSocketAddress.h"
#include "RpcState.h"
+#include "RpcTransportUtils.h"
#include "RpcWireFormat.h"
#include "Utils.h"
#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
-#include <android_runtime/vm.h>
#include <jni.h>
+extern "C" JavaVM* AndroidRuntimeGetJavaVM();
#endif
namespace android {
@@ -147,6 +148,34 @@
return setupSocketClient(UnixSocketAddress(path));
}
+status_t RpcSession::setupUnixDomainSocketBootstrapClient(unique_fd bootstrapFd) {
+ mBootstrapTransport =
+ mCtx->newTransport(RpcTransportFd(std::move(bootstrapFd)), mShutdownTrigger.get());
+ return setupClient([&](const std::vector<uint8_t>& sessionId, bool incoming) {
+ int socks[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, socks) < 0) {
+ int savedErrno = errno;
+ ALOGE("Failed socketpair: %s", strerror(savedErrno));
+ return -savedErrno;
+ }
+ unique_fd clientFd(socks[0]), serverFd(socks[1]);
+
+ int zero = 0;
+ iovec iov{&zero, sizeof(zero)};
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ fds.push_back(std::move(serverFd));
+
+ status_t status = mBootstrapTransport->interruptableWriteFully(mShutdownTrigger.get(), &iov,
+ 1, std::nullopt, &fds);
+ if (status != OK) {
+ ALOGE("Failed to send fd over bootstrap transport: %s", strerror(-status));
+ return status;
+ }
+
+ return initAndAddConnection(RpcTransportFd(std::move(clientFd)), sessionId, incoming);
+ });
+}
+
status_t RpcSession::setupVsockClient(unsigned int cid, unsigned int port) {
return setupSocketClient(VsockSocketAddress(cid, port));
}
@@ -295,16 +324,18 @@
}
void RpcSession::WaitForShutdownListener::onSessionIncomingThreadEnded() {
+ mShutdownCount += 1;
mCv.notify_all();
}
void RpcSession::WaitForShutdownListener::waitForShutdown(RpcMutexUniqueLock& lock,
const sp<RpcSession>& session) {
- while (session->mConnections.mIncoming.size() > 0) {
+ while (mShutdownCount < session->mConnections.mMaxIncoming) {
if (std::cv_status::timeout == mCv.wait_for(lock, std::chrono::seconds(1))) {
ALOGE("Waiting for RpcSession to shut down (1s w/o progress): %zu incoming connections "
- "still.",
- session->mConnections.mIncoming.size());
+ "still %zu/%zu fully shutdown.",
+ session->mConnections.mIncoming.size(), mShutdownCount.load(),
+ session->mConnections.mMaxIncoming);
}
}
}
@@ -377,10 +408,11 @@
"Unable to detach thread. No JavaVM, but it was present before!");
LOG_RPC_DETAIL("Detaching current thread from JVM");
- if (vm->DetachCurrentThread() != JNI_OK) {
+ int ret = vm->DetachCurrentThread();
+ if (ret == JNI_OK) {
mAttached = false;
} else {
- ALOGW("Unable to detach current thread from JVM");
+ ALOGW("Unable to detach current thread from JVM (%d)", ret);
}
}
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index c411f4f..b27f102 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -56,6 +56,7 @@
case RpcSession::FileDescriptorTransportMode::NONE:
return false;
case RpcSession::FileDescriptorTransportMode::UNIX:
+ case RpcSession::FileDescriptorTransportMode::TRUSTY:
return true;
}
}
@@ -886,6 +887,7 @@
it->second.asyncTodo.push(BinderNode::AsyncTodo{
.ref = target,
.data = std::move(transactionData),
+ .ancillaryFds = std::move(ancillaryFds),
.asyncNumber = transaction->asyncNumber,
});
@@ -1046,6 +1048,7 @@
// reset up arguments
transactionData = std::move(todo.data);
+ ancillaryFds = std::move(todo.ancillaryFds);
LOG_ALWAYS_FATAL_IF(target != todo.ref,
"async list should be associated with a binder");
@@ -1205,6 +1208,20 @@
rpcFields->mFds->size(), kMaxFdsPerMsg);
return BAD_VALUE;
}
+ break;
+ }
+ case RpcSession::FileDescriptorTransportMode::TRUSTY: {
+ // Keep this in sync with trusty_ipc.h!!!
+ // We could import that file here on Trusty, but it's not
+ // 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);
+ return BAD_VALUE;
+ }
+ break;
}
}
}
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 7aab5ee..ac86585 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -250,6 +250,7 @@
struct AsyncTodo {
sp<IBinder> ref;
CommandData data;
+ std::vector<std::variant<base::unique_fd, base::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 65e8fac..cd067bf 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -23,6 +23,7 @@
#include <binder/RpcTransportRaw.h>
#include "FdTrigger.h"
+#include "OS.h"
#include "RpcState.h"
#include "RpcTransportUtils.h"
@@ -30,9 +31,6 @@
namespace {
-// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
-constexpr size_t kMaxFdsPerMsg = 253;
-
// RpcTransport with TLS disabled.
class RpcTransportRaw : public RpcTransport {
public:
@@ -63,57 +61,10 @@
override {
bool sentFds = false;
auto send = [&](iovec* iovs, int niovs) -> ssize_t {
- if (ancillaryFds != nullptr && !ancillaryFds->empty() && !sentFds) {
- if (ancillaryFds->size() > kMaxFdsPerMsg) {
- // This shouldn't happen because we check the FD count in RpcState.
- ALOGE("Saw too many file descriptors in RpcTransportCtxRaw: %zu (max is %zu). "
- "Aborting session.",
- ancillaryFds->size(), kMaxFdsPerMsg);
- errno = EINVAL;
- return -1;
- }
-
- // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
- // use memcpy.
- int fds[kMaxFdsPerMsg];
- for (size_t i = 0; i < ancillaryFds->size(); i++) {
- fds[i] = std::visit([](const auto& fd) { return fd.get(); },
- ancillaryFds->at(i));
- }
- const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
-
- alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
-
- msghdr msg{
- .msg_iov = iovs,
- .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
- .msg_control = msgControlBuf,
- .msg_controllen = sizeof(msgControlBuf),
- };
-
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
- memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
-
- msg.msg_controllen = CMSG_SPACE(fdsByteSize);
-
- ssize_t processedSize = TEMP_FAILURE_RETRY(
- sendmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
- if (processedSize > 0) {
- sentFds = true;
- }
- return processedSize;
- }
-
- msghdr msg{
- .msg_iov = iovs,
- // posix uses int, glibc uses size_t. niovs is a
- // non-negative int and can be cast to either.
- .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
- };
- return TEMP_FAILURE_RETRY(sendmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL));
+ ssize_t ret =
+ sendMessageOnSocket(mSocket, iovs, niovs, sentFds ? nullptr : ancillaryFds);
+ sentFds |= ret > 0;
+ return ret;
};
return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, send, "sendmsg", POLLOUT,
altPoll);
@@ -124,54 +75,7 @@
const std::optional<android::base::function_ref<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
auto recv = [&](iovec* iovs, int niovs) -> ssize_t {
- if (ancillaryFds != nullptr) {
- int fdBuffer[kMaxFdsPerMsg];
- alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
-
- msghdr msg{
- .msg_iov = iovs,
- .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
- .msg_control = msgControlBuf,
- .msg_controllen = sizeof(msgControlBuf),
- };
- ssize_t processSize =
- TEMP_FAILURE_RETRY(recvmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL));
- if (processSize < 0) {
- return -1;
- }
-
- for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
- cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
- // application devs to memcpy the data to ensure memory alignment.
- size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
- LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // sanity check
- memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
- 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]));
- }
- break;
- }
- }
-
- if (msg.msg_flags & MSG_CTRUNC) {
- ALOGE("msg was truncated. Aborting session.");
- errno = EPIPE;
- return -1;
- }
-
- return processSize;
- }
- msghdr msg{
- .msg_iov = iovs,
- // posix uses int, glibc uses size_t. niovs is a
- // non-negative int and can be cast to either.
- .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
- };
- return TEMP_FAILURE_RETRY(recvmsg(mSocket.fd.get(), &msg, MSG_NOSIGNAL));
+ return receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds);
};
return interruptableReadOrWrite(mSocket, fdTrigger, iovs, niovs, recv, "recvmsg", POLLIN,
altPoll);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c91d56c..342e4a3 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -58,13 +58,10 @@
"name": "CtsOsTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ "include-filter": "android.os.cts.BinderTest"
},
{
- "exclude-filter": "android.os.cts.BuildTest#testSdkInt"
- },
- {
- "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage"
+ "include-filter": "android.os.cts.ParcelTest"
}
]
},
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 5880c0a..0fb1615 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -114,6 +114,12 @@
@nullable @utf8InCpp String updatableViaApex(@utf8InCpp String name);
/**
+ * Returns all instances which are updatable via the APEX. Instance names are fully qualified
+ * like `pack.age.IFoo/default`.
+ */
+ @utf8InCpp String[] getUpdatableNames(@utf8InCpp String apexName);
+
+ /**
* If connection info is available for the given instance, returns the ConnectionInfo
*/
@nullable ConnectionInfo getConnectionInfo(@utf8InCpp String name);
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+#ifndef BR_TRANSACTION_PENDING_FROZEN
+// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
+#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
+#endif // BR_TRANSACTION_PENDING_FROZEN
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index dc572ac..8cc8105 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -230,7 +230,6 @@
"android.graphicsenv.IGpuService",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
"android.hardware.ICamera",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index c01e92f..d261c21 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -139,12 +139,15 @@
int64_t clearCallingIdentity();
// Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
+ bool hasExplicitIdentity();
+ // For main functions - dangerous for libraries to use
status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
bool flushIfNeeded();
+ // For main functions - dangerous for libraries to use
void joinThreadPool(bool isMain = true);
// Stop the local process.
@@ -241,6 +244,7 @@
bool mPropagateWorkSource;
bool mIsLooper;
bool mIsFlushing;
+ bool mHasExplicitIdentity;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
CallRestriction mCallRestriction;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 413c97f..79e771f 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -115,6 +115,12 @@
virtual std::optional<String16> updatableViaApex(const String16& name) = 0;
/**
+ * Returns all instances which are updatable via the APEX. Instance names are fully qualified
+ * like `pack.age.IFoo/default`.
+ */
+ virtual Vector<String16> getUpdatableNames(const String16& apexName) = 0;
+
+ /**
* If this instance has declared remote connection information, returns
* the ConnectionInfo.
*/
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 6de6ce8..f730acb 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -150,6 +150,9 @@
// Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
binder::Status enforceNoDataAvail() const;
+ // This Api is used by fuzzers to skip dataAvail checks.
+ void setEnforceNoDataAvail(bool enforceNoDataAvail);
+
void freeData();
size_t objectsCount() const;
@@ -1329,6 +1332,9 @@
// data to be overridden with zero when deallocated
mutable bool mDeallocZero;
+ // Set this to false to skip dataAvail checks.
+ bool mEnforceNoDataAvail;
+
release_func mOwner;
size_t mReserved;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9679a5f..bad8cb1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,6 +50,7 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
+ // For main functions - dangerous for libraries to use
void startThreadPool();
bool becomeContextManager();
@@ -57,8 +58,10 @@
sp<IBinder> getStrongProxyForHandle(int32_t handle);
void expungeHandle(int32_t handle, IBinder* binder);
+ // TODO: deprecate.
void spawnPooledThread(bool isMain);
+ // For main functions - dangerous for libraries to use
status_t setThreadPoolMaxThreadCount(size_t maxThreads);
status_t enableOnewaySpamDetection(bool enable);
void giveThreadPoolName();
@@ -94,6 +97,11 @@
*/
size_t getThreadPoolMaxTotalThreadCount() const;
+ /**
+ * Check to see if the thread pool has started.
+ */
+ bool isThreadPoolStarted() const;
+
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 2c99334..4ad0a47 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -50,6 +50,17 @@
std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
/**
+ * Creates an RPC server that bootstraps sessions using an existing
+ * Unix domain socket pair.
+ *
+ * Callers should create a pair of SOCK_STREAM Unix domain sockets, pass
+ * one to RpcServer::setupUnixDomainSocketBootstrapServer and the other
+ * to RpcSession::setupUnixDomainSocketBootstrapClient. Multiple client
+ * session can be created from the client end of the pair.
+ */
+ [[nodiscard]] status_t setupUnixDomainSocketBootstrapServer(base::unique_fd serverFd);
+
+ /**
* This represents a session for responses, e.g.:
*
* process A serves binder a
@@ -60,6 +71,16 @@
[[nodiscard]] status_t setupUnixDomainServer(const char* path);
/**
+ * Sets up an RPC server with a raw socket file descriptor.
+ * The socket should be created and bound to a socket address already, e.g.
+ * the socket can be created in init.rc.
+ *
+ * This method is used in the libbinder_rpc_unstable API
+ * RunInitUnixDomainRpcServer().
+ */
+ [[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);
+
+ /**
* Creates an RPC server at the current port.
*/
[[nodiscard]] status_t setupVsockServer(unsigned int port);
@@ -202,11 +223,18 @@
void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override;
void onSessionIncomingThreadEnded() override;
+ status_t setupExternalServer(
+ base::unique_fd serverFd,
+ std::function<status_t(const RpcServer&, RpcTransportFd*)>&& acceptFn);
+
static constexpr size_t kRpcAddressSize = 128;
static void establishConnection(
sp<RpcServer>&& server, RpcTransportFd clientFd,
std::array<uint8_t, kRpcAddressSize> addr, size_t addrLen,
std::function<void(sp<RpcSession>&&, RpcSession::PreJoinSetupResult&&)>&& joinFn);
+ static status_t acceptSocketConnection(const RpcServer& server, RpcTransportFd* out);
+ static status_t recvmsgSocketConnection(const RpcServer& server, RpcTransportFd* out);
+
[[nodiscard]] status_t setupSocketServer(const RpcSocketAddress& address);
const std::unique_ptr<RpcTransportCtx> mCtx;
@@ -228,6 +256,7 @@
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
RpcConditionVariable mShutdownCv;
+ std::function<status_t(const RpcServer& server, RpcTransportFd* out)> mAcceptFn;
};
} // namespace android
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a25ba98..40faf2c 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -100,6 +100,8 @@
NONE = 0,
// Send file descriptors via unix domain socket ancillary data.
UNIX = 1,
+ // Send file descriptors as Trusty IPC handles.
+ TRUSTY = 2,
};
/**
@@ -115,6 +117,11 @@
[[nodiscard]] status_t setupUnixDomainClient(const char* path);
/**
+ * Connects to an RPC server over a nameless Unix domain socket pair.
+ */
+ [[nodiscard]] status_t setupUnixDomainSocketBootstrapClient(base::unique_fd bootstrap);
+
+ /**
* Connects to an RPC server at the CVD & port.
*/
[[nodiscard]] status_t setupVsockClient(unsigned int cvd, unsigned int port);
@@ -233,6 +240,7 @@
private:
RpcConditionVariable mCv;
+ std::atomic<size_t> mShutdownCount = 0;
};
friend WaitForShutdownListener;
@@ -366,11 +374,14 @@
RpcConditionVariable mAvailableConnectionCv; // for mWaitingThreads
+ std::unique_ptr<RpcTransport> mBootstrapTransport;
+
struct ThreadState {
size_t mWaitingThreads = 0;
// hint index into clients, ++ when sending an async transaction
size_t mOutgoingOffset = 0;
std::vector<sp<RpcConnection>> mOutgoing;
+ // max size of mIncoming. Once any thread starts down, no more can be started.
size_t mMaxIncoming = 0;
std::vector<sp<RpcConnection>> mIncoming;
std::map<RpcMaybeThread::id, RpcMaybeThread> mThreads;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 5baa4d7..f08bde8 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -17,30 +17,56 @@
#pragma once
#include <sys/socket.h>
+#include <stdint.h>
extern "C" {
struct AIBinder;
+struct ARpcServer;
// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server and joins before returning.
-bool RunRpcServer(AIBinder* service, unsigned int port);
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);
-// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
-bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
- void* param);
+// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
+// and a given root IBinder object.
+// The socket should be created in init.rc with the same `name`.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);
+
+// Runs ARpcServer_join() in a background thread. Immediately returns.
+void ARpcServer_start(ARpcServer* server);
+
+// Joins the thread of a running RpcServer instance. At any given point, there
+// can only be one thread calling ARpcServer_join().
+// If a client needs to actively terminate join, call ARpcServer_shutdown() in
+// a separate thread.
+void ARpcServer_join(ARpcServer* server);
+
+// Shuts down any running ARpcServer_join().
+void ARpcServer_shutdown(ARpcServer* server);
+
+// Frees the ARpcServer handle and drops the reference count on the underlying
+// RpcServer instance. The handle must not be reused afterwards.
+// This automatically calls ARpcServer_shutdown().
+void ARpcServer_free(ARpcServer* server);
// Starts an RPC server on a given port and a given root IBinder factory.
-// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of
+// RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of
// assigning single root IBinder object to all connections, factory is called
// whenever a client connects, making it possible to assign unique IBinder
// object to each client.
-bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
- void* factoryContext, unsigned int port);
+bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port);
-AIBinder* RpcClient(unsigned int cid, unsigned int port);
+AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
+
+// Gets the service via the RPC binder with Unix domain socket with the given
+// Unix socket `name`.
+// The final Unix domain socket path name is /dev/socket/`name`.
+AIBinder* UnixDomainRpcClient(const char* name);
// Connect to an RPC server with preconnected file descriptors.
//
@@ -50,5 +76,4 @@
// param will be passed to requestFd. Callers can use param to pass contexts to
// the requestFd function.
AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param);
-
}
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index a3d42b7..f55c779 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -14,24 +14,47 @@
* limitations under the License.
*/
+#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 <cutils/sockets.h>
#include <linux/vm_sockets.h>
using android::OK;
using android::RpcServer;
using android::RpcSession;
+using android::sp;
using android::status_t;
using android::statusToString;
using android::base::unique_fd;
+// Opaque handle for RpcServer.
+struct ARpcServer {};
+
+static sp<RpcServer> toRpcServer(ARpcServer* handle) {
+ auto ref = reinterpret_cast<RpcServer*>(handle);
+ return sp<RpcServer>::fromExisting(ref);
+}
+
+static ARpcServer* createRpcServerHandle(sp<RpcServer>& server) {
+ auto ref = server.get();
+ ref->incStrong(ref);
+ return reinterpret_cast<ARpcServer*>(ref);
+}
+
+static void freeRpcServerHandle(ARpcServer* handle) {
+ auto ref = reinterpret_cast<RpcServer*>(handle);
+ ref->decStrong(ref);
+}
+
extern "C" {
-bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
- void* factoryContext, unsigned int port) {
+bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
+ void* factoryContext, unsigned int port) {
auto server = RpcServer::make();
if (status_t status = server->setupVsockServer(port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
@@ -52,29 +75,50 @@
return true;
}
-bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
- void* param) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
auto server = RpcServer::make();
if (status_t status = server->setupVsockServer(port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
- return false;
+ return nullptr;
}
server->setRootObject(AIBinder_toPlatformBinder(service));
-
- if (readyCallback) readyCallback(param);
- server->join();
-
- // Shutdown any open sessions since server failed.
- (void)server->shutdown();
- return true;
+ return createRpcServerHandle(server);
}
-bool RunRpcServer(AIBinder* service, unsigned int port) {
- return RunRpcServerCallback(service, port, nullptr, nullptr);
+ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
+ auto server = RpcServer::make();
+ auto fd = unique_fd(android_get_control_socket(name));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Failed to get fd for the socket:" << name;
+ return nullptr;
+ }
+ if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
+ LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
+ << " error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ server->setRootObject(AIBinder_toPlatformBinder(service));
+ return createRpcServerHandle(server);
}
-AIBinder* RpcClient(unsigned int cid, unsigned int port) {
+void ARpcServer_start(ARpcServer* handle) {
+ toRpcServer(handle)->start();
+}
+
+void ARpcServer_join(ARpcServer* handle) {
+ toRpcServer(handle)->join();
+}
+
+void ARpcServer_shutdown(ARpcServer* handle) {
+ toRpcServer(handle)->shutdown();
+}
+
+void ARpcServer_free(ARpcServer* handle) {
+ freeRpcServerHandle(handle);
+}
+
+AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
auto session = RpcSession::make();
if (status_t status = session->setupVsockClient(cid, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
@@ -84,6 +128,18 @@
return AIBinder_fromPlatformBinder(session->getRootObject());
}
+AIBinder* UnixDomainRpcClient(const char* name) {
+ std::string pathname(name);
+ pathname = ANDROID_SOCKET_DIR "/" + pathname;
+ auto session = RpcSession::make();
+ 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();
+ return nullptr;
+ }
+ return AIBinder_fromPlatformBinder(session->getRootObject());
+}
+
AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
auto session = RpcSession::make();
auto request = [=] { return unique_fd{requestFd(param)}; };
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index e856569..1bc2416 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -1,8 +1,13 @@
LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
global:
- RunRpcServer;
- RunRpcServerCallback;
- RpcClient;
+ ARpcServer_free;
+ ARpcServer_join;
+ ARpcServer_newInitUnixDomain;
+ ARpcServer_newVsock;
+ ARpcServer_shutdown;
+ ARpcServer_start;
+ VsockRpcClient;
+ UnixDomainRpcClient;
RpcPreconnectedClient;
local:
*;
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 7ea9be7..d6937c2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -30,11 +30,11 @@
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <android/binder_status.h>
-
#include <assert.h>
-
#include <unistd.h>
+
#include <cstddef>
+#include <iostream>
#include <string>
namespace ndk {
@@ -270,14 +270,19 @@
std::string getDescription() const {
#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
if (__builtin_available(android 30, *)) {
-#else
- if (__ANDROID_API__ >= 30) {
#endif
+
+#if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 30
const char* cStr = AStatus_getDescription(get());
std::string ret = cStr;
AStatus_deleteDescription(cStr);
return ret;
+#endif
+
+#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
}
+#endif
+
binder_exception_t exception = getExceptionCode();
std::string desc = std::to_string(exception);
if (exception == EX_SERVICE_SPECIFIC) {
@@ -315,6 +320,11 @@
}
};
+static inline std::ostream& operator<<(std::ostream& os, const ScopedAStatus& status) {
+ return os << status.getDescription();
+ return os;
+}
+
/**
* Convenience wrapper. See AIBinder_DeathRecipient.
*/
@@ -349,7 +359,7 @@
/**
* See AIBinder_Weak_promote.
*/
- SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); }
+ SpAIBinder promote() const { return SpAIBinder(AIBinder_Weak_promote(get())); }
};
namespace internal {
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 78bcb43..9949de2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -196,6 +196,10 @@
bool isRemote() override final { return false; }
+ static std::string makeServiceName(std::string_view instance) {
+ return INTERFACE::descriptor + ("/" + std::string(instance));
+ }
+
protected:
/**
* This function should only be called by asBinder. Otherwise, there is a possibility of
@@ -291,7 +295,10 @@
binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args,
uint32_t numArgs) {
std::shared_ptr<ICInterface> interface = getInterface(binder);
- return interface->dump(fd, args, numArgs);
+ if (interface != nullptr) {
+ return interface->dump(fd, args, numArgs);
+ }
+ return STATUS_DEAD_OBJECT;
}
#ifdef HAS_BINDER_SHELL_COMMAND
@@ -299,7 +306,10 @@
int err, const char** argv,
uint32_t argc) {
std::shared_ptr<ICInterface> interface = getInterface(binder);
- return interface->handleShellCommand(in, out, err, argv, argc);
+ if (interface != nullptr) {
+ return interface->handleShellCommand(in, out, err, argv, argc);
+ }
+ return STATUS_DEAD_OBJECT;
}
#endif
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index c1f2620..caee471 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -41,68 +41,34 @@
if (_status != STATUS_OK) return _status; \
} while (false)
+// AParcelableHolder has been introduced in 31.
+#if __ANDROID_API__ >= 31
class AParcelableHolder {
public:
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
-#if __ANDROID_API__ >= 31
AParcelableHolder(const AParcelableHolder& other)
: mParcel(AParcel_create()), mStability(other.mStability) {
- // AParcelableHolder has been introduced in 31.
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
- AParcel_getDataSize(other.mParcel.get()));
- } else {
- syslog(LOG_ERR,
- "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
- }
+ AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
+ AParcel_getDataSize(other.mParcel.get()));
}
-#endif
AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
binder_status_t writeToParcel(AParcel* parcel) const {
RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- int32_t size = AParcel_getDataSize(this->mParcel.get());
- RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
- } else {
- return STATUS_INVALID_OPERATION;
- }
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- int32_t size = AParcel_getDataSize(this->mParcel.get());
- RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
- } else {
- return STATUS_INVALID_OPERATION;
- }
+ int32_t size = AParcel_getDataSize(this->mParcel.get());
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, size));
+ size = AParcel_getDataSize(this->mParcel.get());
+ RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0, size));
return STATUS_OK;
}
binder_status_t readFromParcel(const AParcel* parcel) {
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_reset(mParcel.get());
- } else {
- return STATUS_INVALID_OPERATION;
- }
+ AParcel_reset(mParcel.get());
parcelable_stability_t wireStability;
RETURN_ON_FAILURE(AParcel_readInt32(parcel, &wireStability));
@@ -123,15 +89,7 @@
return STATUS_BAD_VALUE;
}
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
- } else {
- status = STATUS_INVALID_OPERATION;
- }
+ status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
if (status != STATUS_OK) {
return status;
}
@@ -143,15 +101,7 @@
if (this->mStability > T::_aidl_stability) {
return STATUS_BAD_VALUE;
}
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_reset(mParcel.get());
- } else {
- return STATUS_INVALID_OPERATION;
- }
+ AParcel_reset(mParcel.get());
AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
p.writeToParcel(mParcel.get());
return STATUS_OK;
@@ -161,17 +111,9 @@
binder_status_t getParcelable(std::optional<T>* ret) const {
const std::string parcelableDesc(T::descriptor);
AParcel_setDataPosition(mParcel.get(), 0);
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- if (AParcel_getDataSize(mParcel.get()) == 0) {
- *ret = std::nullopt;
- return STATUS_OK;
- }
- } else {
- return STATUS_INVALID_OPERATION;
+ if (AParcel_getDataSize(mParcel.get()) == 0) {
+ *ret = std::nullopt;
+ return STATUS_OK;
}
std::string parcelableDescInParcel;
binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
@@ -188,18 +130,7 @@
return STATUS_OK;
}
- void reset() {
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- AParcel_reset(mParcel.get());
- } else {
- syslog(LOG_ERR,
- "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
- }
- }
+ void reset() { AParcel_reset(mParcel.get()); }
inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
@@ -207,34 +138,23 @@
inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
-#if __ANDROID_API__ >= 31
inline AParcelableHolder& operator=(const AParcelableHolder& rhs) {
- // AParcelableHolder has been introduced in 31.
-#ifdef __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
- if (__builtin_available(android 31, *)) {
-#else
- if (__ANDROID_API__ >= 31) {
-#endif
- this->reset();
- if (this->mStability != rhs.mStability) {
- syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
- this->mStability, rhs.mStability);
- abort();
- }
- AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
- AParcel_getDataSize(rhs.mParcel.get()));
- } else {
- syslog(LOG_ERR,
- "sdk_version not compatible, AParcelableHolder need sdk_version >= 31!");
+ this->reset();
+ if (this->mStability != rhs.mStability) {
+ syslog(LOG_ERR, "AParcelableHolder stability mismatch: this %d rhs %d!",
+ this->mStability, rhs.mStability);
+ abort();
}
+ AParcel_appendFrom(rhs.mParcel.get(), this->mParcel.get(), 0,
+ AParcel_getDataSize(rhs.mParcel.get()));
return *this;
}
-#endif
private:
mutable ndk::ScopedAParcel mParcel;
parcelable_stability_t mStability;
};
+#endif // __ANDROID_API__ >= 31
#undef RETURN_ON_FAILURE
} // namespace ndk
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index d7840ec..2a00736 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -136,8 +136,10 @@
template <typename _U>
static std::enable_if_t<
#ifdef HAS_NDK_INTERFACE
- std::is_base_of_v<::ndk::ICInterface, _U> ||
- std::is_same_v<::ndk::AParcelableHolder, _U>
+ std::is_base_of_v<::ndk::ICInterface, _U>
+#if __ANDROID_API__ >= 31
+ || std::is_same_v<::ndk::AParcelableHolder, _U>
+#endif
#else
std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
std::is_same_v<os::ParcelFileDescriptor, _U> ||
@@ -158,7 +160,7 @@
template <typename _T>
std::string ToString(const _T& t) {
if constexpr (details::ToEmptyString<_T>::value) {
- return "";
+ return "<unimplemented>";
} else if constexpr (std::is_same_v<bool, _T>) {
return t ? "true" : "false";
} else if constexpr (std::is_same_v<char16_t, _T>) {
@@ -174,9 +176,11 @@
return t;
#ifdef HAS_NDK_INTERFACE
} else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
- return (t.get() == nullptr) ? "(null)" : "";
+ std::stringstream ss;
+ ss << "binder:" << std::hex << t.get();
+ return ss.str();
} else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
- return (t.get() == -1) ? "(null)" : "";
+ return "fd:" + std::to_string(t.get());
#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4163897..1af2119 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -613,7 +613,8 @@
*
* 1. If the binder died, shortly after the call to onBinderDied.
* 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
- * AIBinder_DeathRecipient_delete.
+ * AIBinder_DeathRecipient_delete, after any pending onBinderDied calls
+ * finish.
* 3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
*
* It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
index dfe12a1..74a7157 100644
--- a/libs/binder/ndk/include_platform/android/binder_libbinder.h
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -16,7 +16,7 @@
#pragma once
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)) || defined(__TRUSTY__)
#include <android/binder_ibinder.h>
#include <android/binder_parcel.h>
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index dfa8ea2..ad4188f 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -68,6 +68,7 @@
*
* \param instance identifier of the service used to lookup the service.
*/
+[[deprecated("this polls 5s, use AServiceManager_waitForService or AServiceManager_checkService")]]
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
__INTRODUCED_IN(29);
@@ -108,6 +109,67 @@
__INTRODUCED_IN(31);
/**
+ * Function to call when a service is registered. The instance is passed as well as
+ * ownership of the binder named 'registered'.
+ *
+ * WARNING: a lock is held when this method is called in order to prevent races with
+ * AServiceManager_NotificationRegistration_delete. Do not make synchronous binder calls when
+ * implementing this method to avoid deadlocks.
+ *
+ * \param instance instance name of service registered
+ * \param registered ownership-passed instance of service registered
+ * \param cookie data passed during registration for notifications
+ */
+typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered,
+ void* cookie);
+
+/**
+ * Represents a registration to servicemanager which can be cleared anytime.
+ */
+struct AServiceManager_NotificationRegistration;
+
+/**
+ * Get notifications when a service is registered. If the service is already registered,
+ * you will immediately get a notification.
+ *
+ * WARNING: it is strongly recommended to use AServiceManager_waitForService API instead.
+ * That API will wait synchronously, which is what you usually want in cases, including
+ * using some feature or during boot up. There is a history of bugs where waiting for
+ * notifications like this races with service startup. Also, when this API is used, a service
+ * bug will result in silent failure (rather than a debuggable deadlock). Furthermore, there
+ * is a history of this API being used to know when a service is up as a proxy for whethre
+ * that service should be started. This should only be used if you are intending to get
+ * ahold of the service as a client. For lazy services, whether a service is registered
+ * should not be used as a proxy for when it should be registered, which is only known
+ * by the real client.
+ *
+ * WARNING: if you use this API, you must also ensure that you check missing services are
+ * started and crash otherwise. If service failures are ignored, the system rots.
+ *
+ * \param instance name of service to wait for notifications about
+ * \param onRegister callback for when service is registered
+ * \param cookie data associated with this callback
+ *
+ * \return the token for this registration. Deleting this token will unregister.
+ */
+__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration*
+AServiceManager_registerForServiceNotifications(const char* instance,
+ AServiceManager_onRegister onRegister, void* cookie)
+ __INTRODUCED_IN(34);
+
+/**
+ * Unregister for notifications and delete the object.
+ *
+ * After this method is called, the callback is guaranteed to no longer be invoked. This will block
+ * until any in-progress onRegister callbacks have completed. It is therefore safe to immediately
+ * destroy the void* cookie that was registered when this method returns.
+ *
+ * \param notification object to dismiss
+ */
+void AServiceManager_NotificationRegistration_delete(
+ AServiceManager_NotificationRegistration* notification) __INTRODUCED_IN(34);
+
+/**
* Check if a service is declared (e.g. VINTF manifest).
*
* \param instance identifier of the service.
@@ -143,6 +205,17 @@
bool AServiceManager_isUpdatableViaApex(const char* instance) __INTRODUCED_IN(31);
/**
+ * Returns the APEX name if a service is declared as updatable via an APEX module.
+ *
+ * \param instance identifier of the service
+ * \param context to pass to callback
+ * \param callback taking the APEX name (e.g. 'com.android.foo') and context
+ */
+void AServiceManager_getUpdatableApexName(const char* instance, void* context,
+ void (*callback)(const char*, void*))
+ __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
* Prevent lazy services without client from shutting down their process
*
* This should only be used if it is every eventually set to false. If a
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index f408fad..ffcad55 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -28,17 +28,33 @@
*
* When using this, it is expected that ABinderProcess_setupPolling and
* ABinderProcess_handlePolledCommands are not used.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_startThreadPool();
/**
* This sets the maximum number of threads that can be started in the threadpool. By default, after
* startThreadPool is called, this is 15. If it is called additional times, it will only prevent
* the kernel from starting new threads and will not delete already existing threads.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
/**
+ * Check if the threadpool has already been started.
+ * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually,
+ * you should use this in a library to abort if the threadpool is not started.
+ * Programs should configure binder threadpools once at the beginning.
+ */
+bool ABinderProcess_isThreadPoolStarted();
+/**
* This adds the current thread to the threadpool. This may cause the threadpool to exceed the
* maximum size.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_joinThreadPool();
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 683a433..c1f62e5 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -50,6 +50,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the vendor partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToVendorStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
@@ -79,6 +88,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the system partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToSystemStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 259a736..54e4628 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -152,6 +152,14 @@
AParcel_unmarshal;
};
+LIBBINDER_NDK34 { # introduced=UpsideDownCake
+ global:
+ ABinderProcess_isThreadPoolStarted; # systemapi llndk
+ AServiceManager_getUpdatableApexName; # systemapi
+ AServiceManager_registerForServiceNotifications; # systemapi llndk
+ AServiceManager_NotificationRegistration_delete; # systemapi llndk
+};
+
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c320e8d..8693022 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -129,7 +129,13 @@
}
T* array;
- if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+ if (!allocator(arrayData, length, &array)) {
+ if (length < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ } else {
+ return STATUS_NO_MEMORY;
+ }
+ }
if (length <= 0) return STATUS_OK;
if (array == nullptr) return STATUS_NO_MEMORY;
@@ -157,7 +163,13 @@
}
char16_t* array;
- if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+ if (!allocator(arrayData, length, &array)) {
+ if (length < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ } else {
+ return STATUS_NO_MEMORY;
+ }
+ }
if (length <= 0) return STATUS_OK;
if (array == nullptr) return STATUS_NO_MEMORY;
@@ -204,7 +216,13 @@
return status;
}
- if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
+ if (!allocator(arrayData, length)) {
+ if (length < 0) {
+ return STATUS_UNEXPECTED_NULL;
+ } else {
+ return STATUS_NO_MEMORY;
+ }
+ }
if (length <= 0) return STATUS_OK;
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index ac582a4..bc6610e 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -31,6 +31,9 @@
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
}
+bool ABinderProcess_isThreadPoolStarted() {
+ return ProcessState::self()->isThreadPoolStarted();
+}
void ABinderProcess_joinThreadPool() {
IPCThreadState::self()->joinThreadPool();
}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 7649a26..e107c83 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -28,6 +28,7 @@
using ::android::IServiceManager;
using ::android::sp;
using ::android::status_t;
+using ::android::statusToString;
using ::android::String16;
using ::android::String8;
@@ -86,6 +87,67 @@
AIBinder_incStrong(ret.get());
return ret.get();
}
+typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered,
+ void* cookie);
+
+struct AServiceManager_NotificationRegistration
+ : public IServiceManager::LocalRegistrationCallback {
+ std::mutex m;
+ const char* instance = nullptr;
+ void* cookie = nullptr;
+ AServiceManager_onRegister onRegister = nullptr;
+
+ virtual void onServiceRegistration(const String16& smInstance, const sp<IBinder>& binder) {
+ std::lock_guard<std::mutex> l(m);
+ if (onRegister == nullptr) return;
+
+ CHECK_EQ(String8(smInstance), instance);
+
+ sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder_incStrong(ret.get());
+
+ onRegister(instance, ret.get(), cookie);
+ }
+
+ void clear() {
+ std::lock_guard<std::mutex> l(m);
+ instance = nullptr;
+ cookie = nullptr;
+ onRegister = nullptr;
+ }
+};
+
+__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration*
+AServiceManager_registerForServiceNotifications(const char* instance,
+ AServiceManager_onRegister onRegister,
+ void* cookie) {
+ CHECK_NE(instance, nullptr);
+ CHECK_NE(onRegister, nullptr) << instance;
+ // cookie can be nullptr
+
+ auto cb = sp<AServiceManager_NotificationRegistration>::make();
+ cb->instance = instance;
+ cb->onRegister = onRegister;
+ cb->cookie = cookie;
+
+ 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);
+ return nullptr;
+ }
+
+ cb->incStrong(nullptr);
+ return cb.get();
+}
+
+void AServiceManager_NotificationRegistration_delete(
+ AServiceManager_NotificationRegistration* notification) {
+ CHECK_NE(notification, nullptr);
+ notification->clear();
+ notification->decStrong(nullptr);
+}
+
bool AServiceManager_isDeclared(const char* instance) {
if (instance == nullptr) {
return false;
@@ -113,6 +175,18 @@
sp<IServiceManager> sm = defaultServiceManager();
return sm->updatableViaApex(String16(instance)) != std::nullopt;
}
+void AServiceManager_getUpdatableApexName(const char* instance, void* context,
+ void (*callback)(const char*, void*)) {
+ CHECK_NE(instance, nullptr);
+ // context may be nullptr
+ CHECK_NE(callback, nullptr);
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ std::optional<String16> updatableViaApex = sm->updatableViaApex(String16(instance));
+ if (updatableViaApex.has_value()) {
+ callback(String8(updatableViaApex.value()).c_str(), context);
+ }
+}
void AServiceManager_forceLazyServicesPersist(bool persist) {
auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
serviceRegistrar.forcePersist(persist);
diff --git a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
index f3cd218..43b2cb8 100644
--- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
@@ -106,7 +106,7 @@
std::string outString;
ScopedAStatus status = server->RepeatString("foo", &outString);
EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get()))
- << serviceName << " " << status.getDescription();
+ << serviceName << " " << status;
EXPECT_EQ("foo", outString) << serviceName;
}
}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 6d29238..9d5ef68 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -33,13 +33,15 @@
#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
#include <binder/IShellCallback.h>
-
#include <sys/prctl.h>
+
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
+#include <optional>
#include <thread>
+
#include "android/binder_ibinder.h"
using namespace android;
@@ -252,6 +254,47 @@
AIBinder_decStrong(binder);
}
+struct ServiceData {
+ std::string instance;
+ ndk::SpAIBinder binder;
+
+ static void fillOnRegister(const char* instance, AIBinder* binder, void* cookie) {
+ ServiceData* d = reinterpret_cast<ServiceData*>(cookie);
+ d->instance = instance;
+ d->binder = ndk::SpAIBinder(binder);
+ }
+};
+
+TEST(NdkBinder, RegisterForServiceNotificationsNonExisting) {
+ ServiceData data;
+ auto* notif = AServiceManager_registerForServiceNotifications(
+ "DOES_NOT_EXIST", ServiceData::fillOnRegister, (void*)&data);
+ ASSERT_NE(notif, nullptr);
+
+ sleep(1); // give us a chance to fail
+ AServiceManager_NotificationRegistration_delete(notif);
+
+ // checking after deleting to avoid needing a mutex over the data - otherwise
+ // in an environment w/ multiple threads, you would need to guard access
+ EXPECT_EQ(data.instance, "");
+ EXPECT_EQ(data.binder, nullptr);
+}
+
+TEST(NdkBinder, RegisterForServiceNotificationsExisting) {
+ ServiceData data;
+ auto* notif = AServiceManager_registerForServiceNotifications(
+ kExistingNonNdkService, ServiceData::fillOnRegister, (void*)&data);
+ ASSERT_NE(notif, nullptr);
+
+ sleep(1); // give us a chance to fail
+ AServiceManager_NotificationRegistration_delete(notif);
+
+ // checking after deleting to avoid needing a mutex over the data - otherwise
+ // in an environment w/ multiple threads, you would need to guard access
+ EXPECT_EQ(data.instance, kExistingNonNdkService);
+ EXPECT_EQ(data.binder, ndk::SpAIBinder(AServiceManager_checkService(kExistingNonNdkService)));
+}
+
TEST(NdkBinder, UnimplementedDump) {
sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
ASSERT_NE(foo, nullptr);
@@ -337,6 +380,16 @@
EXPECT_EQ(isUpdatable, false);
}
+TEST(NdkBinder, GetUpdatableViaApex) {
+ std::optional<std::string> updatableViaApex;
+ AServiceManager_getUpdatableApexName(
+ "android.hardware.light.ILights/default", &updatableViaApex,
+ [](const char* apexName, void* context) {
+ *static_cast<std::optional<std::string>*>(context) = apexName;
+ });
+ EXPECT_EQ(updatableViaApex, std::nullopt) << *updatableViaApex;
+}
+
// This is too slow
TEST(NdkBinder, CheckLazyServiceShutDown) {
ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService));
@@ -670,6 +723,26 @@
EXPECT_EQ(42, pparcel->readInt32());
}
+TEST(NdkBinder, GetAndVerifyScopedAIBinder_Weak) {
+ for (const ndk::SpAIBinder& binder :
+ {// remote
+ ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)),
+ // local
+ ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) {
+ // get a const ScopedAIBinder_Weak and verify promote
+ EXPECT_NE(binder.get(), nullptr);
+ const ndk::ScopedAIBinder_Weak wkAIBinder =
+ ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get()));
+ EXPECT_EQ(wkAIBinder.promote().get(), binder.get());
+ // get another ScopedAIBinder_Weak and verify
+ ndk::ScopedAIBinder_Weak wkAIBinder2 =
+ ndk::ScopedAIBinder_Weak(AIBinder_Weak_new(binder.get()));
+ EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder.get(), wkAIBinder2.get()));
+ EXPECT_FALSE(AIBinder_Weak_lt(wkAIBinder2.get(), wkAIBinder.get()));
+ EXPECT_EQ(wkAIBinder2.promote(), wkAIBinder.promote());
+ }
+}
+
class MyResultReceiver : public BnResultReceiver {
public:
Mutex mMutex;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index a135796..afd414a 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -17,7 +17,6 @@
rustlibs: [
"libbinder_ndk_sys",
"libdowncast_rs",
- "liblazy_static",
"liblibc",
],
host_supported: true,
@@ -88,6 +87,7 @@
min_sdk_version: "Tiramisu",
lints: "none",
clippy_lints: "none",
+ visibility: [":__subpackages__"],
}
rust_bindgen {
@@ -159,7 +159,6 @@
rustlibs: [
"libbinder_ndk_sys",
"libdowncast_rs",
- "liblazy_static",
"liblibc",
],
}
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 5ebc27f..f70ebfc 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -19,7 +19,9 @@
"libbinder_rpc_unstable_bindgen_sys",
"libbinder_rs",
"libdowncast_rs",
+ "libforeign_types",
"liblibc",
+ "liblog_rust",
],
apex_available: [
"com.android.compos",
diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs
index 743800b..48c787b 100644
--- a/libs/binder/rust/rpcbinder/src/client.rs
+++ b/libs/binder/rust/rpcbinder/src/client.rs
@@ -15,6 +15,7 @@
*/
use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong};
+use std::ffi::CString;
use std::os::{
raw::{c_int, c_void},
unix::io::RawFd,
@@ -22,9 +23,9 @@
/// Connects to an RPC Binder server over vsock.
pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
- // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can
- // safely be taken by new_spibinder.
- unsafe { new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, port)) }
+ // SAFETY: AIBinder returned by VsockRpcClient has correct reference count,
+ // and the ownership can safely be taken by new_spibinder.
+ unsafe { new_spibinder(binder_rpc_unstable_bindgen::VsockRpcClient(cid, port)) }
}
/// Connects to an RPC Binder server for a particular interface over vsock.
@@ -35,6 +36,27 @@
interface_cast(get_vsock_rpc_service(cid, port))
}
+/// Connects to an RPC Binder server over Unix domain socket.
+pub fn get_unix_domain_rpc_service(socket_name: &str) -> Option<SpIBinder> {
+ let socket_name = match CString::new(socket_name) {
+ Ok(s) => s,
+ Err(e) => {
+ log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
+ return None;
+ }
+ };
+ // SAFETY: AIBinder returned by UnixDomainRpcClient has correct reference count,
+ // and the ownership can safely be taken by new_spibinder.
+ unsafe { new_spibinder(binder_rpc_unstable_bindgen::UnixDomainRpcClient(socket_name.as_ptr())) }
+}
+
+/// Connects to an RPC Binder server for a particular interface over Unix domain socket.
+pub fn get_unix_domain_rpc_interface<T: FromIBinder + ?Sized>(
+ socket_name: &str,
+) -> Result<Strong<T>, StatusCode> {
+ interface_cast(get_unix_domain_rpc_service(socket_name))
+}
+
/// Connects to an RPC Binder server, using the given callback to get (and take ownership of)
/// file descriptors already connected to it.
pub fn get_preconnected_rpc_service(
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index a5eea61..1b719aa 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -20,7 +20,7 @@
mod server;
pub use client::{
- get_preconnected_rpc_interface, get_preconnected_rpc_service, get_vsock_rpc_interface,
- get_vsock_rpc_service,
+ get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface,
+ get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service,
};
-pub use server::{run_rpc_server, run_rpc_server_with_factory};
+pub use server::{run_vsock_rpc_server_with_factory, RpcServer, RpcServerRef};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index aeb23c6..42f5567 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -18,68 +18,89 @@
unstable_api::{AIBinder, AsNative},
SpIBinder,
};
-use std::{os::raw, ptr::null_mut};
+use binder_rpc_unstable_bindgen::ARpcServer;
+use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::io::{Error, ErrorKind};
+use std::{ffi::CString, os::raw, ptr::null_mut};
-/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
-/// port.
-///
-/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
-/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
-/// attempt to connect.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
-where
- F: FnOnce(),
-{
- let mut ready_notifier = ReadyNotifier(Some(on_ready));
- ready_notifier.run_server(service, port)
+foreign_type! {
+ type CType = binder_rpc_unstable_bindgen::ARpcServer;
+ fn drop = binder_rpc_unstable_bindgen::ARpcServer_free;
+
+ /// A type that represents a foreign instance of RpcServer.
+ #[derive(Debug)]
+ pub struct RpcServer;
+ /// A borrowed RpcServer.
+ pub struct RpcServerRef;
}
-struct ReadyNotifier<F>(Option<F>)
-where
- F: FnOnce();
+/// SAFETY - The opaque handle can be cloned freely.
+unsafe impl Send for RpcServer {}
+/// SAFETY - The underlying C++ RpcServer class is thread-safe.
+unsafe impl Sync for RpcServer {}
-impl<F> ReadyNotifier<F>
-where
- F: FnOnce(),
-{
- fn run_server(&mut self, mut service: SpIBinder, port: u32) -> bool {
+impl RpcServer {
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// vsock port.
+ pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> {
let service = service.as_native_mut();
- let param = self.as_void_ptr();
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
- // RunRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only
- // uses them before it returns, which is during the lifetime of `self`.
unsafe {
- binder_rpc_unstable_bindgen::RunRpcServerCallback(
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port))
+ }
+ }
+
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// socket file name. The socket should be initialized in init.rc with the same name.
+ pub fn new_init_unix_domain(
+ mut service: SpIBinder,
+ socket_name: &str,
+ ) -> Result<RpcServer, Error> {
+ let socket_name = match CString::new(socket_name) {
+ Ok(s) => s,
+ Err(e) => {
+ log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
+ return Err(Error::from(ErrorKind::InvalidInput));
+ }
+ };
+ let service = service.as_native_mut();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ unsafe {
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain(
service,
- port,
- Some(Self::ready_callback),
- param,
- )
+ socket_name.as_ptr(),
+ ))
}
}
- fn as_void_ptr(&mut self) -> *mut raw::c_void {
- self as *mut _ as *mut raw::c_void
- }
-
- unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
- // SAFETY: This is only ever called by `RunRpcServerCallback`, within the lifetime of the
- // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly
- // aligned non-null pointer to an initialized instance).
- let ready_notifier = param as *mut Self;
- ready_notifier.as_mut().unwrap().notify()
- }
-
- fn notify(&mut self) {
- if let Some(on_ready) = self.0.take() {
- on_ready();
+ unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> {
+ if ptr.is_null() {
+ return Err(Error::new(ErrorKind::Other, "Failed to start server"));
}
+ Ok(RpcServer::from_ptr(ptr))
+ }
+}
+
+impl RpcServerRef {
+ /// Starts a new background thread and calls join(). Returns immediately.
+ pub fn start(&self) {
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
+ }
+
+ /// Joins the RpcServer thread. The call blocks until the server terminates.
+ /// This must be called from exactly one thread.
+ pub fn join(&self) {
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) };
+ }
+
+ /// Shuts down the running RpcServer. Can be called multiple times and from
+ /// multiple threads. Called automatically during drop().
+ pub fn shutdown(&self) {
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) };
}
}
@@ -91,7 +112,7 @@
/// The current thread is joined to the binder thread pool to handle incoming messages.
///
/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_rpc_server_with_factory(
+pub fn run_vsock_rpc_server_with_factory(
port: u32,
mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync,
) -> bool {
@@ -100,18 +121,22 @@
let mut factory_ref: RpcServerFactoryRef = &mut factory;
let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void;
- // SAFETY: `factory_wrapper` is only ever called by `RunRpcServerWithFactory`, with context
+ // SAFETY: `factory_wrapper` is only ever called by `RunVsockRpcServerWithFactory`, with context
// taking the pointer value above (so a properly aligned non-null pointer to an initialized
// `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made
- // after `RunRpcServerWithFactory` returns).
+ // after `RunVsockRpcServerWithFactory` returns).
unsafe {
- binder_rpc_unstable_bindgen::RunRpcServerWithFactory(Some(factory_wrapper), context, port)
+ binder_rpc_unstable_bindgen::RunVsockRpcServerWithFactory(
+ Some(factory_wrapper),
+ context,
+ port,
+ )
}
}
unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder {
// SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by
- // `run_rpc_server_with_factory`, and we are still within the lifetime of the value it is
+ // `run_vsock_rpc_server_with_factory`, and we are still within the lifetime of the value it is
// pointing to.
let factory_ptr = context as *mut RpcServerFactoryRef;
let factory = factory_ptr.as_mut().unwrap();
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 63c8684..976f54d 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1091,7 +1091,7 @@
}
} => {
$( #[$attr] )*
- #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+ #[derive(Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub struct $enum(pub $backing);
impl $enum {
@@ -1104,6 +1104,15 @@
}
}
+ impl std::fmt::Debug for $enum {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self.0 {
+ $($value => f.write_str(stringify!($name)),)*
+ _ => f.write_fmt(format_args!("{}", self.0))
+ }
+ }
+ }
+
impl $crate::binder_impl::Serialize for $enum {
fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> {
parcel.write(&self.0)
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 195d9ac..a0e61d9 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -148,4 +148,5 @@
pub use crate::binder::AsNative;
pub use crate::proxy::unstable_api::new_spibinder;
pub use crate::sys::AIBinder;
+ pub use crate::sys::AParcel;
}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index dee05d0..6f686fb 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -22,7 +22,6 @@
use crate::proxy::SpIBinder;
use crate::sys;
-use lazy_static::lazy_static;
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fs::File;
@@ -508,10 +507,8 @@
_private: (),
}
-lazy_static! {
- // Count of how many LazyServiceGuard objects are in existence.
- static ref GUARD_COUNT: Mutex<u64> = Mutex::new(0);
-}
+// Count of how many LazyServiceGuard objects are in existence.
+static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
impl LazyServiceGuard {
/// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 4e10fa9..ca2cedc 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -502,7 +502,7 @@
let instances = binder::get_declared_instances("android.hardware.light.ILights")
.expect("Could not get declared instances");
- let expected_defaults = if has_lights { 1 } else { 0 };
+ let expected_defaults = usize::from(has_lights);
assert_eq!(expected_defaults, instances.iter().filter(|i| i.as_str() == "default").count());
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
new file mode 100644
index 0000000..28e0200
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -0,0 +1,25 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_fuzz {
+ name: "parcel_fuzzer_rs",
+ srcs: [
+ "parcel_fuzzer.rs",
+ ],
+ rustlibs: [
+ "libarbitrary",
+ "libnum_traits",
+ "libbinder_rs",
+ "libbinder_random_parcel_rs",
+ "binderReadParcelIface-rust",
+ ],
+
+ fuzz_config: {
+ cc: [
+ "waghpawan@google.com",
+ "smoreland@google.com",
+ ],
+ },
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
new file mode 100644
index 0000000..c5c7719
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#![allow(missing_docs)]
+#![no_main]
+
+#[macro_use]
+extern crate libfuzzer_sys;
+
+mod read_utils;
+
+use crate::read_utils::READ_FUNCS;
+use binder::binder_impl::{
+ Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode,
+};
+use binder::{
+ declare_binder_interface, BinderFeatures, Interface, Parcelable, ParcelableHolder, SpIBinder,
+ StatusCode,
+};
+use binder_random_parcel_rs::create_random_parcel;
+use libfuzzer_sys::arbitrary::Arbitrary;
+
+#[derive(Arbitrary, Debug)]
+enum ReadOperation {
+ SetDataPosition { pos: i32 },
+ GetDataSize,
+ ReadParcelableHolder { is_vintf: bool },
+ ReadBasicTypes { instructions: Vec<usize> },
+}
+
+#[derive(Arbitrary, Debug)]
+enum Operation<'a> {
+ Transact { code: u32, flag: u32, data: &'a [u8] },
+ Append { start: i32, len: i32, data1: &'a [u8], data2: &'a [u8], append_all: bool },
+ Read { read_operations: Vec<ReadOperation>, data: &'a [u8] },
+}
+
+/// Interface to fuzz transact with random parcel
+pub trait BinderTransactTest: Interface {}
+
+declare_binder_interface! {
+ BinderTransactTest["Binder_Transact_Test"] {
+ native: BnBinderTransactTest(on_transact),
+ proxy: BpBinderTransactTest,
+ }
+}
+
+impl BinderTransactTest for Binder<BnBinderTransactTest> {}
+
+impl BinderTransactTest for BpBinderTransactTest {}
+
+impl BinderTransactTest for () {}
+
+fn on_transact(
+ _service: &dyn BinderTransactTest,
+ _code: TransactionCode,
+ _parcel: &BorrowedParcel<'_>,
+ _reply: &mut BorrowedParcel<'_>,
+) -> Result<(), StatusCode> {
+ Err(StatusCode::UNKNOWN_ERROR)
+}
+
+fn do_transact(code: u32, data: &[u8], flag: u32) {
+ let p: Parcel = create_random_parcel(data);
+ let spibinder: Option<SpIBinder> =
+ Some(BnBinderTransactTest::new_binder((), BinderFeatures::default()).as_binder());
+ let _reply = spibinder.submit_transact(code, p, flag);
+}
+
+fn do_append_fuzz(start: i32, len: i32, data1: &[u8], data2: &[u8], append_all: bool) {
+ let mut p1 = create_random_parcel(data1);
+ let p2 = create_random_parcel(data2);
+
+ // Fuzz both append methods
+ if append_all {
+ match p1.append_all_from(&p2) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("Error occurred while appending a parcel using append_all_from: {:?}", e)
+ }
+ }
+ } else {
+ match p1.append_from(&p2, start, len) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("Error occurred while appending a parcel using append_from: {:?}", e)
+ }
+ }
+ };
+}
+
+fn do_read_fuzz(read_operations: Vec<ReadOperation>, data: &[u8]) {
+ let parcel = create_random_parcel(data);
+
+ for operation in read_operations {
+ match operation {
+ ReadOperation::SetDataPosition { pos } => {
+ unsafe {
+ // Safety: Safe if pos is less than current size of the parcel.
+ // It relies on C++ code for bound checks
+ match parcel.set_data_position(pos) {
+ Ok(result) => result,
+ Err(e) => println!("error occurred while setting data position: {:?}", e),
+ }
+ }
+ }
+
+ ReadOperation::GetDataSize => {
+ let data_size = parcel.get_data_size();
+ println!("data size from parcel: {:?}", data_size);
+ }
+
+ ReadOperation::ReadParcelableHolder { is_vintf } => {
+ let stability = if is_vintf { Stability::Vintf } else { Stability::Local };
+ let mut holder: ParcelableHolder = ParcelableHolder::new(stability);
+ match holder.read_from_parcel(parcel.borrowed_ref()) {
+ Ok(result) => result,
+ Err(err) => {
+ println!("error occurred while reading from parcel: {:?}", err)
+ }
+ }
+ }
+
+ ReadOperation::ReadBasicTypes { instructions } => {
+ for instruction in instructions.iter() {
+ let read_index = instruction % READ_FUNCS.len();
+ READ_FUNCS[read_index](parcel.borrowed_ref());
+ }
+ }
+ }
+ }
+}
+
+fuzz_target!(|operation: Operation| {
+ match operation {
+ Operation::Transact { code, flag, data } => {
+ do_transact(code, data, flag);
+ }
+
+ Operation::Append { start, len, data1, data2, append_all } => {
+ do_append_fuzz(start, len, data1, data2, append_all);
+ }
+
+ Operation::Read { read_operations, data } => {
+ do_read_fuzz(read_operations, data);
+ }
+ }
+});
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
new file mode 100644
index 0000000..43a3094
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/Android.bp
@@ -0,0 +1,52 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_bindgen {
+ name: "libbinder_random_parcel_bindgen",
+ crate_name: "binder_random_parcel_bindgen",
+ host_supported: true,
+ wrapper_src: "wrappers/RandomParcelWrapper.hpp",
+ source_stem: "bindings",
+ visibility: [":__subpackages__"],
+ bindgen_flags: [
+ "--size_t-is-usize",
+ "--allowlist-function",
+ "createRandomParcel",
+ "--allowlist-function",
+ "fuzzRustService",
+ ],
+ shared_libs: [
+ "libc++",
+ "libbinder_ndk",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+}
+
+rust_library {
+ name: "libbinder_random_parcel_rs",
+ crate_name: "binder_random_parcel_rs",
+ host_supported: true,
+ srcs: [
+ "src/lib.rs",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libcutils",
+ "libc++",
+ ],
+ static_libs: [
+ "libbinder_create_parcel",
+ "libbinder_random_parcel",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ "libbinder_random_parcel_bindgen",
+ ],
+ lints: "none",
+ clippy_lints: "none",
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
new file mode 100644
index 0000000..43e407c
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -0,0 +1,33 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+aidl_interface {
+ name: "testServiceInterface",
+ srcs: ["ITestService.aidl"],
+ unstable: true,
+ backend: {
+ rust: {
+ enabled: true,
+ },
+ },
+}
+
+rust_fuzz {
+ name: "example_service_fuzzer",
+ srcs: [
+ "service_fuzzer.rs",
+ ],
+ rustlibs: [
+ "libbinder_rs",
+ "libbinder_random_parcel_rs",
+ "testServiceInterface-rust",
+ ],
+ fuzz_config: {
+ cc: [
+ "waghpawan@google.com",
+ "smoreland@google.com",
+ ],
+ },
+}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl
similarity index 89%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl
index d62891b..8ce6558 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/ITestService.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-parcelable SingleDataParcelable{
- int data;
+interface ITestService {
+ boolean repeatData(boolean token);
}
\ No newline at end of file
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs
new file mode 100644
index 0000000..a427f28
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/service_fuzzer.rs
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#![allow(missing_docs)]
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+
+use binder::{self, BinderFeatures, Interface};
+use binder_random_parcel_rs::fuzz_service;
+use testServiceInterface::aidl::ITestService::{self, BnTestService};
+
+struct TestService;
+
+impl Interface for TestService {}
+
+impl ITestService::ITestService for TestService {
+ fn repeatData(&self, token: bool) -> binder::Result<bool> {
+ Ok(token)
+ }
+}
+
+fuzz_target!(|data: &[u8]| {
+ let service = BnTestService::new_binder(TestService, BinderFeatures::default());
+ fuzz_service(&mut service.as_binder(), data);
+});
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
new file mode 100644
index 0000000..1bbd674
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/src/lib.rs
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+use binder::binder_impl::Parcel;
+use binder::unstable_api::{AParcel, AsNative};
+use binder::SpIBinder;
+use binder_random_parcel_bindgen::{createRandomParcel, fuzzRustService};
+use std::os::raw::c_void;
+
+/// This API creates a random parcel to be used by fuzzers
+pub fn create_random_parcel(fuzzer_data: &[u8]) -> Parcel {
+ let mut parcel = Parcel::new();
+ let aparcel_ptr: *mut AParcel = parcel.as_native_mut();
+ let ptr = aparcel_ptr as *mut c_void;
+ unsafe {
+ // Safety: `Parcel::as_native_mut` and `slice::as_ptr` always
+ // return valid pointers.
+ createRandomParcel(ptr, fuzzer_data.as_ptr(), fuzzer_data.len());
+ }
+ parcel
+}
+
+/// This API automatically fuzzes provided service
+pub fn fuzz_service(binder: &mut SpIBinder, fuzzer_data: &[u8]) {
+ let ptr = binder.as_native_mut() as *mut c_void;
+ unsafe {
+ // Safety: `SpIBinder::as_native_mut` and `slice::as_ptr` always
+ // return valid pointers.
+ fuzzRustService(ptr, fuzzer_data.as_ptr(), fuzzer_data.len());
+ }
+}
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
similarity index 64%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
index fc2542b..831bd56 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/wrappers/RandomParcelWrapper.hpp
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <cstdint>
+#include <cstddef>
-parcelable GenericDataParcelable {
- int data;
- float majorVersion;
- float minorVersion;
- IBinder binder;
- ParcelFileDescriptor fileDescriptor;
- int[] array;
+extern "C" {
+ // This API is used by rust to fill random parcel.
+ void createRandomParcel(void* aParcel, const uint8_t* data, size_t len);
+
+ // This API is used by fuzzers to automatically fuzz aidl services
+ void fuzzRustService(void* binder, const uint8_t* data, size_t len);
}
\ No newline at end of file
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
new file mode 100644
index 0000000..a2d48b6
--- /dev/null
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+use binder::binder_impl::BorrowedParcel;
+use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
+use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable;
+
+macro_rules! read_parcel_interface {
+ ($data_type:ty) => {
+ |parcel: &BorrowedParcel<'_>| {
+ let _res = parcel.read::<$data_type>();
+ }
+ };
+}
+
+#[derive(Debug, Default)]
+pub struct SomeParcelable {
+ pub data: i32,
+}
+
+impl binder::Parcelable for SomeParcelable {
+ fn write_to_parcel(
+ &self,
+ parcel: &mut binder::binder_impl::BorrowedParcel,
+ ) -> std::result::Result<(), binder::StatusCode> {
+ parcel.sized_write(|subparcel| subparcel.write(&self.data))
+ }
+
+ fn read_from_parcel(
+ &mut self,
+ parcel: &binder::binder_impl::BorrowedParcel,
+ ) -> std::result::Result<(), binder::StatusCode> {
+ parcel.sized_read(|subparcel| match subparcel.read() {
+ Ok(result) => {
+ self.data = result;
+ Ok(())
+ }
+ Err(e) => Err(e),
+ })
+ }
+}
+
+binder::impl_deserialize_for_parcelable!(SomeParcelable);
+
+pub const READ_FUNCS: &[fn(&BorrowedParcel<'_>)] = &[
+ //read basic types
+ read_parcel_interface!(bool),
+ read_parcel_interface!(i8),
+ read_parcel_interface!(i32),
+ read_parcel_interface!(i64),
+ read_parcel_interface!(f32),
+ read_parcel_interface!(f64),
+ read_parcel_interface!(u16),
+ read_parcel_interface!(u32),
+ read_parcel_interface!(u64),
+ read_parcel_interface!(String),
+ //read vec of basic types
+ read_parcel_interface!(Vec<i8>),
+ read_parcel_interface!(Vec<i32>),
+ read_parcel_interface!(Vec<i64>),
+ read_parcel_interface!(Vec<f32>),
+ read_parcel_interface!(Vec<f64>),
+ read_parcel_interface!(Vec<u16>),
+ read_parcel_interface!(Vec<u32>),
+ read_parcel_interface!(Vec<u64>),
+ read_parcel_interface!(Vec<String>),
+ read_parcel_interface!(Option<Vec<i8>>),
+ read_parcel_interface!(Option<Vec<i32>>),
+ read_parcel_interface!(Option<Vec<i64>>),
+ read_parcel_interface!(Option<Vec<f32>>),
+ read_parcel_interface!(Option<Vec<f64>>),
+ read_parcel_interface!(Option<Vec<u16>>),
+ read_parcel_interface!(Option<Vec<u32>>),
+ read_parcel_interface!(Option<Vec<u64>>),
+ read_parcel_interface!(Option<Vec<String>>),
+ read_parcel_interface!(ParcelFileDescriptor),
+ read_parcel_interface!(Vec<Option<ParcelFileDescriptor>>),
+ read_parcel_interface!(Option<Vec<ParcelFileDescriptor>>),
+ read_parcel_interface!(Option<Vec<Option<ParcelFileDescriptor>>>),
+ read_parcel_interface!(SpIBinder),
+ read_parcel_interface!(Vec<Option<SpIBinder>>),
+ read_parcel_interface!(Option<Vec<SpIBinder>>),
+ read_parcel_interface!(Option<Vec<Option<SpIBinder>>>),
+ read_parcel_interface!(SomeParcelable),
+ read_parcel_interface!(Vec<Option<SomeParcelable>>),
+ read_parcel_interface!(Option<Vec<SomeParcelable>>),
+ read_parcel_interface!(Option<Vec<Option<SomeParcelable>>>),
+ // Fuzz read_from_parcel for AIDL generated parcelables
+ |parcel| {
+ let mut empty_parcelable: EmptyParcelable = EmptyParcelable::default();
+ match empty_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => {
+ println!("EmptyParcelable: error occurred while reading from a parcel: {:?}", e)
+ }
+ }
+ },
+ |parcel| {
+ let mut single_parcelable: SingleDataParcelable = SingleDataParcelable::default();
+ match single_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => println!(
+ "SingleDataParcelable: error occurred while reading from a parcel: {:?}",
+ e
+ ),
+ }
+ },
+ |parcel| {
+ let mut generic_parcelable: GenericDataParcelable = GenericDataParcelable::default();
+ match generic_parcelable.read_from_parcel(parcel) {
+ Ok(result) => result,
+ Err(e) => println!(
+ "GenericDataParcelable: error occurred while reading from a parcel: {:?}",
+ e
+ ),
+ }
+ },
+];
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 777f3c9..692cc95 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -156,6 +156,10 @@
std::optional<std::string>* _aidl_return) override {
return mImpl->updatableViaApex(name, _aidl_return);
}
+ android::binder::Status getUpdatableNames(const std::string& apexName,
+ std::vector<std::string>* _aidl_return) override {
+ return mImpl->getUpdatableNames(apexName, _aidl_return);
+ }
android::binder::Status getConnectionInfo(
const std::string& name,
std::optional<android::os::ConnectionInfo>* _aidl_return) override {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 92d132f..a999d59 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -232,6 +232,7 @@
srcs: [
"binderRpcTest.cpp",
"binderRpcTestCommon.cpp",
+ "binderRpcUniversalTests.cpp",
],
test_suites: ["general-tests"],
@@ -722,5 +723,7 @@
"smoreland@google.com",
"waghpawan@google.com",
],
+ // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/tests/BinderRpcTestServerConfig.aidl b/libs/binder/tests/BinderRpcTestServerConfig.aidl
index 34d74be..b2e0ef2 100644
--- a/libs/binder/tests/BinderRpcTestServerConfig.aidl
+++ b/libs/binder/tests/BinderRpcTestServerConfig.aidl
@@ -21,5 +21,6 @@
int rpcSecurity;
int serverVersion;
int vsockPort;
+ int socketFd; // Inherited from the parent process.
@utf8InCpp String addr;
}
diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl
index b15a225..a3ed571 100644
--- a/libs/binder/tests/IBinderRpcTest.aidl
+++ b/libs/binder/tests/IBinderRpcTest.aidl
@@ -71,4 +71,13 @@
ParcelFileDescriptor echoAsFile(@utf8InCpp String content);
ParcelFileDescriptor concatFiles(in List<ParcelFileDescriptor> files);
+
+ // FDs sent via `blockingSendFdOneway` can be received via
+ // `blockingRecvFd`. The handler for `blockingSendFdOneway` will block
+ // until the next `blockingRecvFd` call.
+ //
+ // This is useful for carefully controlling how/when oneway transactions
+ // get queued.
+ oneway void blockingSendFdOneway(in ParcelFileDescriptor fd);
+ ParcelFileDescriptor blockingRecvFd();
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 6e1c8ac..f7498c4 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -115,10 +115,12 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_ECHO_VECTOR,
+ BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
BINDER_LIB_TEST_REJECT_OBJECTS,
BINDER_LIB_TEST_CAN_GET_SID,
BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+ BINDER_LIB_TEST_IS_THREADPOOL_STARTED,
BINDER_LIB_TEST_LOCK_UNLOCK,
BINDER_LIB_TEST_PROCESS_LOCK,
BINDER_LIB_TEST_UNLOCK_AFTER_MS,
@@ -1158,6 +1160,21 @@
EXPECT_EQ(readValue, testValue);
}
+TEST_F(BinderLibTest, FileDescriptorRemainsNonBlocking) {
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ Parcel reply;
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_NON_BLOCKING_FD, {} /*data*/, &reply),
+ StatusEq(NO_ERROR));
+ base::unique_fd fd;
+ EXPECT_THAT(reply.readUniqueFileDescriptor(&fd), StatusEq(OK));
+
+ const int result = fcntl(fd.get(), F_GETFL);
+ ASSERT_NE(result, -1);
+ EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
// see ProcessState.cpp BINDER_VM_SIZE = 1MB.
// This value is not exposed, but some code in the framework relies on being able to use
// buffers near the cap size.
@@ -1367,6 +1384,14 @@
EXPECT_EQ(replyi, kKernelThreads + 1);
}
+TEST_F(BinderLibTest, ThreadPoolStarted) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR);
+ EXPECT_TRUE(reply.readBool());
+}
+
size_t epochMillis() {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
@@ -1801,6 +1826,28 @@
reply->writeUint64Vector(vector);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_NON_BLOCKING_FD: {
+ std::array<int, 2> sockets;
+ const bool created = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets.data()) == 0;
+ if (!created) {
+ ALOGE("Could not create socket pair");
+ return UNKNOWN_ERROR;
+ }
+
+ const int result = fcntl(sockets[0], F_SETFL, O_NONBLOCK);
+ if (result != 0) {
+ ALOGE("Could not make socket non-blocking: %s", strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+ base::unique_fd out(sockets[0]);
+ status_t writeResult = reply->writeUniqueFileDescriptor(out);
+ if (writeResult != NO_ERROR) {
+ ALOGE("Could not write unique_fd");
+ return writeResult;
+ }
+ close(sockets[1]); // we don't need the other side of the fd
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_REJECT_OBJECTS: {
return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
}
@@ -1811,6 +1858,10 @@
reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
return NO_ERROR;
}
+ case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: {
+ reply->writeBool(ProcessState::self()->isThreadPoolStarted());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_PROCESS_LOCK: {
m_blockMutex.lock();
return NO_ERROR;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 9a6d4df..02aa45f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -15,7 +15,6 @@
*/
#include <android-base/stringprintf.h>
-#include <gtest/gtest.h>
#include <chrono>
#include <cstdlib>
@@ -29,6 +28,7 @@
#include <sys/socket.h>
#include "binderRpcTestCommon.h"
+#include "binderRpcTestFixture.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -44,37 +44,6 @@
constexpr bool kEnableSharedLibs = true;
#endif
-static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
- RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
-
-TEST(BinderRpcParcel, EntireParcelFormatted) {
- Parcel p;
- p.writeInt32(3);
-
- EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), "format must be set before data is written");
-}
-
-TEST(BinderRpc, CannotUseNextWireVersion) {
- auto session = RpcSession::make();
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
- EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
-}
-
-TEST(BinderRpc, CanUseExperimentalWireVersion) {
- auto session = RpcSession::make();
- EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
-}
-
-using android::binder::Status;
-
-#define EXPECT_OK(status) \
- do { \
- Status stat = (status); \
- EXPECT_TRUE(stat.isOk()) << stat; \
- } while (false)
-
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
@@ -92,7 +61,15 @@
class Process {
public:
- Process(Process&&) = default;
+ Process(Process&& other)
+ : mCustomExitStatusCheck(std::move(other.mCustomExitStatusCheck)),
+ mReadEnd(std::move(other.mReadEnd)),
+ mWriteEnd(std::move(other.mWriteEnd)) {
+ // The default move constructor doesn't clear mPid after moving it,
+ // which we need to do because the destructor checks for mPid!=0
+ 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;
@@ -152,21 +129,26 @@
return vsockPort++;
}
-struct ProcessSession {
+static base::unique_fd initUnixSocket(std::string addr) {
+ auto socket_addr = UnixSocketAddress(addr.c_str());
+ base::unique_fd fd(
+ TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX)));
+ CHECK(fd.ok());
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize())));
+ return fd;
+}
+
+// Destructors need to be defined, even if pure virtual
+ProcessSession::~ProcessSession() {}
+
+class LinuxProcessSession : public ProcessSession {
+public:
// reference to process hosting a socket server
Process host;
- struct SessionInfo {
- sp<RpcSession> session;
- sp<IBinder> root;
- };
-
- // client session objects associated with other process
- // each one represents a separate session
- std::vector<SessionInfo> sessions;
-
- ProcessSession(ProcessSession&&) = default;
- ~ProcessSession() {
+ LinuxProcessSession(LinuxProcessSession&&) = default;
+ LinuxProcessSession(Process&& host) : host(std::move(host)) {}
+ ~LinuxProcessSession() override {
for (auto& session : sessions) {
session.root = nullptr;
}
@@ -181,50 +163,28 @@
wp<RpcSession> weakSession = session;
session = nullptr;
- EXPECT_EQ(nullptr, weakSession.promote())
- << (debugBacktrace(host.getPid()), debugBacktrace(getpid()), "Leaked session");
- }
- }
-};
+ // b/244325464 - 'getStrongCount' is printing '1' on failure here, which indicates the
+ // the object should not actually be promotable. By looping, we distinguish a race here
+ // from a bug causing the object to not be promotable.
+ for (size_t i = 0; i < 3; i++) {
+ sp<RpcSession> strongSession = weakSession.promote();
+ EXPECT_EQ(nullptr, strongSession)
+ << (debugBacktrace(host.getPid()), debugBacktrace(getpid()),
+ "Leaked sess: ")
+ << strongSession->getStrongCount() << " checked time " << i;
-// Process session where the process hosts IBinderRpcTest, the server used
-// for most testing here
-struct BinderRpcTestProcessSession {
- ProcessSession proc;
-
- // pre-fetched root object (for first session)
- sp<IBinder> rootBinder;
-
- // pre-casted root object (for first session)
- sp<IBinderRpcTest> rootIface;
-
- // whether session should be invalidated by end of run
- bool expectAlreadyShutdown = false;
-
- BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
- ~BinderRpcTestProcessSession() {
- if (!expectAlreadyShutdown) {
- EXPECT_NE(nullptr, rootIface);
- if (rootIface == nullptr) return;
-
- std::vector<int32_t> remoteCounts;
- // calling over any sessions counts across all sessions
- EXPECT_OK(rootIface->countBinders(&remoteCounts));
- EXPECT_EQ(remoteCounts.size(), proc.sessions.size());
- for (auto remoteCount : remoteCounts) {
- EXPECT_EQ(remoteCount, 1);
- }
-
- // even though it is on another thread, shutdown races with
- // the transaction reply being written
- if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ if (strongSession != nullptr) {
+ sleep(1);
+ }
}
}
-
- rootIface = nullptr;
- rootBinder = nullptr;
}
+
+ void setCustomExitStatusCheck(std::function<void(int wstatus)> f) override {
+ host.setCustomExitStatusCheck(std::move(f));
+ }
+
+ void terminate() override { host.terminate(); }
};
static base::unique_fd connectTo(const RpcSocketAddress& addr) {
@@ -242,531 +202,157 @@
return serverFd;
}
-using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
- android::base::borrowed_fd readEnd);
-
-class BinderRpc : public ::testing::TestWithParam<
- std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
-public:
- SocketType socketType() const { return std::get<0>(GetParam()); }
- RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
- uint32_t clientVersion() const { return std::get<2>(GetParam()); }
- uint32_t serverVersion() const { return std::get<3>(GetParam()); }
- bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
- bool noKernel() const { return std::get<5>(GetParam()); }
-
- bool clientOrServerSingleThreaded() const {
- return !kEnableRpcThreads || serverSingleThreaded();
+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);
}
- // Whether the test params support sending FDs in parcels.
- bool supportsFdTransport() const {
- return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
- (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX);
+ int zero = 0;
+ iovec iov{&zero, sizeof(zero)};
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ fds.emplace_back(std::move(sockServer));
+
+ if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
+ }
+ return std::move(sockClient);
+}
+
+std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+ auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
+ std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
+ if (singleThreaded) {
+ ret += "_single_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ }
+ return ret;
+}
+
+// This creates a new process serving an interface on a certain number of
+// threads.
+std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
+ const BinderRpcOptions& options) {
+ CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
+
+ SocketType socketType = std::get<0>(GetParam());
+ RpcSecurity rpcSecurity = std::get<1>(GetParam());
+ uint32_t clientVersion = std::get<2>(GetParam());
+ uint32_t serverVersion = std::get<3>(GetParam());
+ bool singleThreaded = std::get<4>(GetParam());
+ bool noKernel = std::get<5>(GetParam());
+
+ 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" : "");
+
+ base::unique_fd bootstrapClientFd, socketFd;
+
+ auto addr = allocateSocketAddress();
+ // Initializes the socket before the fork/exec.
+ if (socketType == SocketType::UNIX_RAW) {
+ socketFd = initUnixSocket(addr);
+ } 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);
+ }
}
- static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
- auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
- auto ret = PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" +
- std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
- if (singleThreaded) {
- ret += "_single_threaded";
- }
- if (noKernel) {
- ret += "_no_kernel";
- }
- return ret;
+ auto ret = std::make_unique<LinuxProcessSession>(
+ Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) {
+ 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);
+ }));
+
+ BinderRpcTestServerConfig serverConfig;
+ serverConfig.numThreads = options.numThreads;
+ serverConfig.socketType = static_cast<int32_t>(socketType);
+ serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity);
+ serverConfig.serverVersion = serverVersion;
+ serverConfig.vsockPort = allocateVsockPort();
+ serverConfig.addr = addr;
+ serverConfig.socketFd = socketFd.get();
+ for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
+ serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
+ static_cast<int32_t>(mode));
+ }
+ writeToFd(ret->host.writeEnd(), serverConfig);
+
+ std::vector<sp<RpcSession>> sessions;
+ auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
+ for (size_t i = 0; i < options.numSessions; i++) {
+ sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
}
- // This creates a new process serving an interface on a certain number of
- // threads.
- ProcessSession createRpcTestSocketServerProcessEtc(const BinderRpcOptions& options) {
- CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
+ auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
+ BinderRpcTestClientInfo clientInfo;
+ for (const auto& session : sessions) {
+ auto& parcelableCert = clientInfo.certs.emplace_back();
+ parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+ }
+ writeToFd(ret->host.writeEnd(), clientInfo);
- SocketType socketType = std::get<0>(GetParam());
- RpcSecurity rpcSecurity = std::get<1>(GetParam());
- uint32_t clientVersion = std::get<2>(GetParam());
- uint32_t serverVersion = std::get<3>(GetParam());
- bool singleThreaded = std::get<4>(GetParam());
- bool noKernel = std::get<5>(GetParam());
+ CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+ if (socketType == SocketType::INET) {
+ CHECK_NE(0, serverInfo.port);
+ }
- 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" : "");
+ if (rpcSecurity == RpcSecurity::TLS) {
+ const auto& serverCert = serverInfo.cert.data;
+ CHECK_EQ(OK,
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
+ }
- auto ret = ProcessSession{
- .host = Process([=](android::base::borrowed_fd writeEnd,
- android::base::borrowed_fd readEnd) {
- 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);
- }),
- };
+ status_t status;
- BinderRpcTestServerConfig serverConfig;
- serverConfig.numThreads = options.numThreads;
- serverConfig.socketType = static_cast<int32_t>(socketType);
- serverConfig.rpcSecurity = static_cast<int32_t>(rpcSecurity);
- serverConfig.serverVersion = serverVersion;
- serverConfig.vsockPort = allocateVsockPort();
- serverConfig.addr = allocateSocketAddress();
- for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
- serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
- static_cast<int32_t>(mode));
- }
- writeToFd(ret.host.writeEnd(), serverConfig);
+ for (const auto& session : sessions) {
+ CHECK(session->setProtocolVersion(clientVersion));
+ session->setMaxIncomingThreads(options.numIncomingConnections);
+ session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
- std::vector<sp<RpcSession>> sessions;
- auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
- for (size_t i = 0; i < options.numSessions; i++) {
- sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
- }
-
- auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd());
- BinderRpcTestClientInfo clientInfo;
- for (const auto& session : sessions) {
- auto& parcelableCert = clientInfo.certs.emplace_back();
- parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
- }
- writeToFd(ret.host.writeEnd(), clientInfo);
-
- CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
- if (socketType == SocketType::INET) {
- CHECK_NE(0, serverInfo.port);
- }
-
- if (rpcSecurity == RpcSecurity::TLS) {
- const auto& serverCert = serverInfo.cert.data;
- CHECK_EQ(OK,
- certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
- serverCert));
- }
-
- status_t status;
-
- for (const auto& session : sessions) {
- CHECK(session->setProtocolVersion(clientVersion));
- session->setMaxIncomingThreads(options.numIncomingConnections);
- session->setMaxOutgoingThreads(options.numOutgoingConnections);
- session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
-
- switch (socketType) {
- case SocketType::PRECONNECTED:
- status = session->setupPreconnectedClient({}, [=]() {
- return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
- });
- break;
- case SocketType::UNIX:
- status = session->setupUnixDomainClient(serverConfig.addr.c_str());
- break;
- case SocketType::VSOCK:
- status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
- break;
- case SocketType::INET:
- status = session->setupInetClient("127.0.0.1", serverInfo.port);
- break;
- default:
- LOG_ALWAYS_FATAL("Unknown socket type");
- }
- if (options.allowConnectFailure && status != OK) {
- ret.sessions.clear();
+ switch (socketType) {
+ case SocketType::PRECONNECTED:
+ status = session->setupPreconnectedClient({}, [=]() {
+ return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
+ });
break;
- }
- CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
- ret.sessions.push_back({session, session->getRootObject()});
+ case SocketType::UNIX_RAW:
+ case SocketType::UNIX:
+ status = session->setupUnixDomainClient(serverConfig.addr.c_str());
+ break;
+ case SocketType::UNIX_BOOTSTRAP:
+ status = session->setupUnixDomainSocketBootstrapClient(
+ base::unique_fd(dup(bootstrapClientFd.get())));
+ break;
+ case SocketType::VSOCK:
+ status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
+ break;
+ case SocketType::INET:
+ status = session->setupInetClient("127.0.0.1", serverInfo.port);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
}
- return ret;
+ if (options.allowConnectFailure && status != OK) {
+ ret->sessions.clear();
+ break;
+ }
+ CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
+ ret->sessions.push_back({session, session->getRootObject()});
}
-
- BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
- BinderRpcTestProcessSession ret{
- .proc = createRpcTestSocketServerProcessEtc(options),
- };
-
- ret.rootBinder = ret.proc.sessions.empty() ? nullptr : ret.proc.sessions.at(0).root;
- ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
-
- return ret;
- }
-
- void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs = 500);
-};
-
-TEST_P(BinderRpc, Ping) {
- auto proc = createRpcTestSocketServerProcess({});
- ASSERT_NE(proc.rootBinder, nullptr);
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-}
-
-TEST_P(BinderRpc, GetInterfaceDescriptor) {
- auto proc = createRpcTestSocketServerProcess({});
- ASSERT_NE(proc.rootBinder, nullptr);
- EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
-}
-
-TEST_P(BinderRpc, MultipleSessions) {
- if (serverSingleThreaded()) {
- // Tests with multiple sessions require a multi-threaded service,
- // but work fine on a single-threaded client
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
- for (auto session : proc.proc.sessions) {
- ASSERT_NE(nullptr, session.root);
- EXPECT_EQ(OK, session.root->pingBinder());
- }
-}
-
-TEST_P(BinderRpc, SeparateRootObject) {
- if (serverSingleThreaded()) {
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- SocketType type = std::get<0>(GetParam());
- if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
- // we can't get port numbers for unix sockets
- return;
- }
-
- auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
-
- int port1 = 0;
- EXPECT_OK(proc.rootIface->getClientPort(&port1));
-
- sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root);
- int port2;
- EXPECT_OK(rootIface2->getClientPort(&port2));
-
- // we should have a different IBinderRpcTest object created for each
- // session, because we use setPerSessionRootObject
- EXPECT_NE(port1, port2);
-}
-
-TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
- auto proc = createRpcTestSocketServerProcess({});
- Parcel data;
- Parcel reply;
- EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
-}
-
-TEST_P(BinderRpc, AppendSeparateFormats) {
- auto proc1 = createRpcTestSocketServerProcess({});
- auto proc2 = createRpcTestSocketServerProcess({});
-
- Parcel pRaw;
-
- Parcel p1;
- p1.markForBinder(proc1.rootBinder);
- p1.writeInt32(3);
-
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
- EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
-
- Parcel p2;
- p2.markForBinder(proc2.rootBinder);
- p2.writeInt32(7);
-
- EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
- EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
-}
-
-TEST_P(BinderRpc, UnknownTransaction) {
- auto proc = createRpcTestSocketServerProcess({});
- Parcel data;
- data.markForBinder(proc.rootBinder);
- Parcel reply;
- EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
-}
-
-TEST_P(BinderRpc, SendSomethingOneway) {
- auto proc = createRpcTestSocketServerProcess({});
- EXPECT_OK(proc.rootIface->sendString("asdf"));
-}
-
-TEST_P(BinderRpc, SendAndGetResultBack) {
- auto proc = createRpcTestSocketServerProcess({});
- std::string doubled;
- EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
- EXPECT_EQ("cool cool ", doubled);
-}
-
-TEST_P(BinderRpc, SendAndGetResultBackBig) {
- auto proc = createRpcTestSocketServerProcess({});
- std::string single = std::string(1024, 'a');
- std::string doubled;
- EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
- EXPECT_EQ(single + single, doubled);
-}
-
-TEST_P(BinderRpc, InvalidNullBinderReturn) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
-}
-
-TEST_P(BinderRpc, CallMeBack) {
- auto proc = createRpcTestSocketServerProcess({});
-
- int32_t pingResult;
- EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
- EXPECT_EQ(OK, pingResult);
-
- EXPECT_EQ(0, MyBinderRpcSession::gNum);
-}
-
-TEST_P(BinderRpc, RepeatBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> inBinder = new MyBinderRpcSession("foo");
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
- EXPECT_EQ(inBinder, outBinder);
-
- wp<IBinder> weak = inBinder;
- inBinder = nullptr;
- outBinder = nullptr;
-
- // Force reading a reply, to process any pending dec refs from the other
- // process (the other process will process dec refs there before processing
- // the ping here).
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
- EXPECT_EQ(nullptr, weak.promote());
-
- EXPECT_EQ(0, MyBinderRpcSession::gNum);
-}
-
-TEST_P(BinderRpc, RepeatTheirBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
-
- sp<IBinder> inBinder = IInterface::asBinder(session);
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
- EXPECT_EQ(inBinder, outBinder);
-
- wp<IBinder> weak = inBinder;
- session = nullptr;
- inBinder = nullptr;
- outBinder = nullptr;
-
- // Force reading a reply, to process any pending dec refs from the other
- // process (the other process will process dec refs there before processing
- // the ping here).
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
- EXPECT_EQ(nullptr, weak.promote());
-}
-
-TEST_P(BinderRpc, RepeatBinderNull) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
- EXPECT_EQ(nullptr, outBinder);
-}
-
-TEST_P(BinderRpc, HoldBinder) {
- auto proc = createRpcTestSocketServerProcess({});
-
- IBinder* ptr = nullptr;
- {
- sp<IBinder> binder = new BBinder();
- ptr = binder.get();
- EXPECT_OK(proc.rootIface->holdBinder(binder));
- }
-
- sp<IBinder> held;
- EXPECT_OK(proc.rootIface->getHeldBinder(&held));
-
- EXPECT_EQ(held.get(), ptr);
-
- // stop holding binder, because we test to make sure references are cleaned
- // up
- EXPECT_OK(proc.rootIface->holdBinder(nullptr));
- // and flush ref counts
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-}
-
-// START TESTS FOR LIMITATIONS OF SOCKET BINDER
-// These are behavioral differences form regular binder, where certain usecases
-// aren't supported.
-
-TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
- auto proc1 = createRpcTestSocketServerProcess({});
- auto proc2 = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
-}
-
-TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
- if (serverSingleThreaded()) {
- GTEST_SKIP() << "This test requires a multi-threaded service";
- }
-
- auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
-
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder)
- .transactionError());
-}
-
-TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
- if (!kEnableKernelIpc || noKernel()) {
- GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
- "at build time.";
- }
-
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
- sp<IBinder> outBinder;
- EXPECT_EQ(INVALID_OPERATION,
- proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
-}
-
-TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
- if (!kEnableKernelIpc || noKernel()) {
- GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
- "at build time.";
- }
-
- auto proc = createRpcTestSocketServerProcess({});
-
- // for historical reasons, IServiceManager interface only returns the
- // exception code
- EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
- defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
-}
-
-// END TESTS FOR LIMITATIONS OF SOCKET BINDER
-
-TEST_P(BinderRpc, RepeatRootObject) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> outBinder;
- EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
- EXPECT_EQ(proc.rootBinder, outBinder);
-}
-
-TEST_P(BinderRpc, NestedTransactions) {
- auto proc = createRpcTestSocketServerProcess({
- // Enable FD support because it uses more stack space and so represents
- // something closer to a worst case scenario.
- .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
- .serverSupportedFileDescriptorTransportModes =
- {RpcSession::FileDescriptorTransportMode::UNIX},
- });
-
- auto nastyNester = sp<MyBinderRpcTest>::make();
- EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
-
- wp<IBinder> weak = nastyNester;
- nastyNester = nullptr;
- EXPECT_EQ(nullptr, weak.promote());
-}
-
-TEST_P(BinderRpc, SameBinderEquality) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> a;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
-
- sp<IBinder> b;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
-
- EXPECT_EQ(a, b);
-}
-
-TEST_P(BinderRpc, SameBinderEqualityWeak) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinder> a;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
- wp<IBinder> weak = a;
- a = nullptr;
-
- sp<IBinder> b;
- EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
-
- // this is the wrong behavior, since BpBinder
- // doesn't implement onIncStrongAttempted
- // but make sure there is no crash
- EXPECT_EQ(nullptr, weak.promote());
-
- GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
-
- // In order to fix this:
- // - need to have incStrongAttempted reflected across IPC boundary (wait for
- // response to promote - round trip...)
- // - sendOnLastWeakRef, to delete entries out of RpcState table
- EXPECT_EQ(b, weak.promote());
-}
-
-#define expectSessions(expected, iface) \
- do { \
- int session; \
- EXPECT_OK((iface)->getNumOpenSessions(&session)); \
- EXPECT_EQ(expected, session); \
- } while (false)
-
-TEST_P(BinderRpc, SingleSession) {
- auto proc = createRpcTestSocketServerProcess({});
-
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
- std::string out;
- EXPECT_OK(session->getName(&out));
- EXPECT_EQ("aoeu", out);
-
- expectSessions(1, proc.rootIface);
- session = nullptr;
- expectSessions(0, proc.rootIface);
-}
-
-TEST_P(BinderRpc, ManySessions) {
- auto proc = createRpcTestSocketServerProcess({});
-
- std::vector<sp<IBinderRpcSession>> sessions;
-
- for (size_t i = 0; i < 15; i++) {
- expectSessions(i, proc.rootIface);
- sp<IBinderRpcSession> session;
- EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
- sessions.push_back(session);
- }
- expectSessions(sessions.size(), proc.rootIface);
- for (size_t i = 0; i < sessions.size(); i++) {
- std::string out;
- EXPECT_OK(sessions.at(i)->getName(&out));
- EXPECT_EQ(std::to_string(i), out);
- }
- expectSessions(sessions.size(), proc.rootIface);
-
- while (!sessions.empty()) {
- sessions.pop_back();
- expectSessions(sessions.size(), proc.rootIface);
- }
- expectSessions(0, proc.rootIface);
-}
-
-size_t epochMillis() {
- using std::chrono::duration_cast;
- using std::chrono::milliseconds;
- using std::chrono::seconds;
- using std::chrono::system_clock;
- return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+ return ret;
}
TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
@@ -786,12 +372,12 @@
ts.push_back(std::thread([&] { proc.rootIface->lockUnlock(); }));
}
- usleep(100000); // give chance for calls on other threads
+ usleep(10000); // give chance for calls on other threads
// other calls still work
EXPECT_EQ(OK, proc.rootBinder->pingBinder());
- constexpr size_t blockTimeMs = 500;
+ constexpr size_t blockTimeMs = 50;
size_t epochMsBefore = epochMillis();
// after this, we should never see a response within this time
EXPECT_OK(proc.rootIface->unlockInMsAsync(blockTimeMs));
@@ -805,8 +391,8 @@
for (auto& t : ts) t.join();
}
-void BinderRpc::testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
+ size_t sleepMs = 500) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -906,18 +492,43 @@
saturateThreadPool(kNumServerThreads, proc.rootIface);
}
-TEST_P(BinderRpc, OnewayCallDoesNotWait) {
- constexpr size_t kReallyLongTimeMs = 100;
- constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
+TEST_P(BinderRpc, OnewayCallQueueingWithFds) {
+ if (!supportsFdTransport()) {
+ GTEST_SKIP() << "Would fail trivially (which is tested elsewhere)";
+ }
+ if (clientOrServerSingleThreaded()) {
+ GTEST_SKIP() << "This test requires multiple threads";
+ }
- auto proc = createRpcTestSocketServerProcess({});
+ // This test forces a oneway transaction to be queued by issuing two
+ // `blockingSendFdOneway` calls, then drains the queue by issuing two
+ // `blockingRecvFd` calls.
+ //
+ // For more details about the queuing semantics see
+ // https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
- size_t epochMsBefore = epochMillis();
+ auto proc = createRpcTestSocketServerProcess({
+ .numThreads = 3,
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
- EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
+ EXPECT_OK(proc.rootIface->blockingSendFdOneway(
+ android::os::ParcelFileDescriptor(mockFileDescriptor("a"))));
+ EXPECT_OK(proc.rootIface->blockingSendFdOneway(
+ android::os::ParcelFileDescriptor(mockFileDescriptor("b"))));
- size_t epochMsAfter = epochMillis();
- EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
+ android::os::ParcelFileDescriptor fdA;
+ EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA));
+ std::string result;
+ CHECK(android::base::ReadFdToString(fdA.get(), &result));
+ EXPECT_EQ(result, "a");
+
+ android::os::ParcelFileDescriptor fdB;
+ EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
+ CHECK(android::base::ReadFdToString(fdB.get(), &result));
+ EXPECT_EQ(result, "b");
}
TEST_P(BinderRpc, OnewayCallQueueing) {
@@ -966,7 +577,7 @@
// Build up oneway calls on the second session to make sure it terminates
// and shuts down. The first session should be unaffected (proc destructor
// checks the first session).
- auto iface = interface_cast<IBinderRpcTest>(proc.proc.sessions.at(1).root);
+ auto iface = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
std::vector<std::thread> threads;
for (size_t i = 0; i < kNumClients; i++) {
@@ -994,66 +605,7 @@
// any pending commands). We need to erase this session from the record
// here, so that the destructor for our session won't check that this
// session is valid, but we still want it to test the other session.
- proc.proc.sessions.erase(proc.proc.sessions.begin() + 1);
-}
-
-TEST_P(BinderRpc, Callbacks) {
- const static std::string kTestString = "good afternoon!";
-
- for (bool callIsOneway : {true, false}) {
- for (bool callbackIsOneway : {true, false}) {
- for (bool delayed : {true, false}) {
- if (clientOrServerSingleThreaded() &&
- (callIsOneway || callbackIsOneway || delayed)) {
- // we have no incoming connections to receive the callback
- continue;
- }
-
- size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
- auto proc = createRpcTestSocketServerProcess(
- {.numThreads = 1,
- .numSessions = 1,
- .numIncomingConnections = numIncomingConnections});
- auto cb = sp<MyBinderRpcCallback>::make();
-
- if (callIsOneway) {
- EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
- kTestString));
- } else {
- EXPECT_OK(
- proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
- }
-
- // if both transactions are synchronous and the response is sent back on the
- // same thread, everything should have happened in a nested call. Otherwise,
- // the callback will be processed on another thread.
- if (callIsOneway || callbackIsOneway || delayed) {
- using std::literals::chrono_literals::operator""s;
- RpcMutexUniqueLock _l(cb->mMutex);
- cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
- }
-
- EXPECT_EQ(cb->mValues.size(), 1)
- << "callIsOneway: " << callIsOneway
- << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
- if (cb->mValues.empty()) continue;
- EXPECT_EQ(cb->mValues.at(0), kTestString)
- << "callIsOneway: " << callIsOneway
- << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
-
- // since we are severing the connection, we need to go ahead and
- // tell the server to shutdown and exit so that waitpid won't hang
- if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
- EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
- }
-
- // since this session has an incoming connection w/ a threadpool, we
- // need to manually shut it down
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
- proc.expectAlreadyShutdown = true;
- }
- }
- }
+ proc.proc->sessions.erase(proc.proc->sessions.begin() + 1);
}
TEST_P(BinderRpc, SingleDeathRecipient) {
@@ -1083,10 +635,10 @@
}
std::unique_lock<std::mutex> lock(dr->mMtx);
- ASSERT_TRUE(dr->mCv.wait_for(lock, 1000ms, [&]() { return dr->dead; }));
+ ASSERT_TRUE(dr->mCv.wait_for(lock, 100ms, [&]() { return dr->dead; }));
// need to wait for the session to shutdown so we don't "Leak session"
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
proc.expectAlreadyShutdown = true;
}
@@ -1114,16 +666,16 @@
// Explicitly calling shutDownAndWait will cause the death recipients
// to be called.
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
std::unique_lock<std::mutex> lock(dr->mMtx);
if (!dr->dead) {
- EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 1000ms));
+ EXPECT_EQ(std::cv_status::no_timeout, dr->mCv.wait_for(lock, 100ms));
}
EXPECT_TRUE(dr->dead) << "Failed to receive the death notification.";
- proc.proc.host.terminate();
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ proc.proc->terminate();
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1168,18 +720,10 @@
}
// need to wait for the session to shutdown so we don't "Leak session"
- EXPECT_TRUE(proc.proc.sessions.at(0).session->shutdownAndWait(true));
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
proc.expectAlreadyShutdown = true;
}
-TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
- auto proc = createRpcTestSocketServerProcess({});
- auto cb = sp<MyBinderRpcCallback>::make();
-
- Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
- EXPECT_EQ(WOULD_BLOCK, status.transactionError());
-}
-
TEST_P(BinderRpc, Die) {
for (bool doDeathCleanup : {true, false}) {
auto proc = createRpcTestSocketServerProcess({});
@@ -1195,7 +739,7 @@
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError())
<< "Do death cleanup: " << doDeathCleanup;
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1225,7 +769,7 @@
// second time! we catch the error :)
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGABRT)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1239,9 +783,9 @@
{RpcSession::FileDescriptorTransportMode::UNIX},
.allowConnectFailure = true,
});
- EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
- proc.proc.host.terminate();
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed";
+ proc.proc->terminate();
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1255,9 +799,9 @@
{RpcSession::FileDescriptorTransportMode::NONE},
.allowConnectFailure = true,
});
- EXPECT_TRUE(proc.proc.sessions.empty()) << "session connections should have failed";
- proc.proc.host.terminate();
- proc.proc.host.setCustomExitStatusCheck([](int wstatus) {
+ EXPECT_TRUE(proc.proc->sessions.empty()) << "session connections should have failed";
+ proc.proc->terminate();
+ proc.proc->setCustomExitStatusCheck([](int wstatus) {
EXPECT_TRUE(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == SIGTERM)
<< "server process failed incorrectly: " << WaitStatusToString(wstatus);
});
@@ -1369,6 +913,33 @@
EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
}
+TEST_P(BinderRpc, AppendInvalidFd) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ int badFd = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 0);
+ ASSERT_NE(badFd, -1);
+
+ // Close the file descriptor so it becomes invalid for dup
+ close(badFd);
+
+ Parcel p1;
+ p1.markForBinder(proc.rootBinder);
+ p1.writeInt32(3);
+ EXPECT_EQ(OK, p1.writeFileDescriptor(badFd, false));
+
+ Parcel pRaw;
+ pRaw.markForBinder(proc.rootBinder);
+ EXPECT_EQ(OK, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
+ pRaw.setDataPosition(0);
+ EXPECT_EQ(3, pRaw.readInt32());
+ ASSERT_EQ(-1, pRaw.readFileDescriptor());
+}
+
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
if constexpr (!kEnableSharedLibs) {
GTEST_SKIP() << "Test disabled because Binder was built as a static library";
@@ -1425,16 +996,6 @@
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
-TEST_P(BinderRpc, AidlDelegatorTest) {
- auto proc = createRpcTestSocketServerProcess({});
- auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
- ASSERT_NE(nullptr, myDelegator);
-
- std::string doubled;
- EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
- EXPECT_EQ("cool cool ", doubled);
-}
-
static bool testSupportVsockLoopback() {
// We don't need to enable TLS to know if vsock is supported.
unsigned int vsockPort = allocateVsockPort();
@@ -1525,7 +1086,8 @@
}
static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
- std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+ std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET,
+ SocketType::UNIX_RAW};
if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
@@ -1701,7 +1263,7 @@
bool shutdown = false;
for (int i = 0; i < 10 && !shutdown; i++) {
- usleep(300 * 1000); // 300ms; total 3s
+ usleep(30 * 1000); // 30ms; total 300ms
if (server->shutdown()) shutdown = true;
}
ASSERT_TRUE(shutdown) << "server->shutdown() never returns true";
@@ -1726,6 +1288,8 @@
// A server that handles client socket connections.
class Server {
public:
+ using AcceptConnection = std::function<base::unique_fd(Server*)>;
+
explicit Server() {}
Server(Server&&) = default;
~Server() { shutdownAndWait(); }
@@ -1750,6 +1314,32 @@
return connectTo(UnixSocketAddress(addr.c_str()));
};
} break;
+ case SocketType::UNIX_BOOTSTRAP: {
+ base::unique_fd bootstrapFdClient, bootstrapFdServer;
+ if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) {
+ return AssertionFailure() << "Socketpair() failed";
+ }
+ auto status = rpcServer->setupUnixDomainSocketBootstrapServer(
+ std::move(bootstrapFdServer));
+ if (status != OK) {
+ return AssertionFailure() << "setupUnixDomainSocketBootstrapServer: "
+ << statusToString(status);
+ }
+ mBootstrapSocket = RpcTransportFd(std::move(bootstrapFdClient));
+ mAcceptConnection = &Server::recvmsgServerConnection;
+ mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); };
+ } break;
+ case SocketType::UNIX_RAW: {
+ auto addr = allocateSocketAddress();
+ auto status = rpcServer->setupRawSocketServer(initUnixSocket(addr));
+ if (status != OK) {
+ return AssertionFailure()
+ << "setupRawSocketServer: " << statusToString(status);
+ }
+ mConnectToServer = [addr] {
+ return connectTo(UnixSocketAddress(addr.c_str()));
+ };
+ } break;
case SocketType::VSOCK: {
auto port = allocateVsockPort();
auto status = rpcServer->setupVsockServer(port);
@@ -1797,14 +1387,33 @@
LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
mThread = std::make_unique<std::thread>(&Server::run, this);
}
+
+ base::unique_fd acceptServerConnection() {
+ return base::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;
+ int buf;
+ iovec iov{&buf, sizeof(buf)};
+
+ if (receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+ }
+ if (fds.size() != 1) {
+ LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
+ }
+ return std::move(std::get<base::unique_fd>(fds[0]));
+ }
+
void run() {
LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
std::vector<std::thread> threads;
while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
- base::unique_fd acceptedFd(
- TEMP_FAILURE_RETRY(accept4(mFd.fd.get(), nullptr, nullptr /*length*/,
- SOCK_CLOEXEC | SOCK_NONBLOCK)));
+ base::unique_fd acceptedFd = mAcceptConnection(this);
threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
}
@@ -1831,8 +1440,9 @@
private:
std::unique_ptr<std::thread> mThread;
ConnectToServer mConnectToServer;
+ AcceptConnection mAcceptConnection = &Server::acceptServerConnection;
std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
- RpcTransportFd mFd;
+ RpcTransportFd mFd, mBootstrapSocket;
std::unique_ptr<RpcTransportCtx> mCtx;
std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
std::make_shared<RpcCertificateVerifierSimple>();
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 4513d36..654e16c 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -49,6 +49,7 @@
#include "../BuildFlags.h"
#include "../FdTrigger.h"
+#include "../OS.h" // for testing UnixBootstrap clients
#include "../RpcSocketAddress.h" // for testing preconnected clients
#include "../RpcState.h" // for debugging
#include "../vm_sockets.h" // for VMADDR_*
@@ -67,15 +68,22 @@
enum class SocketType {
PRECONNECTED,
UNIX,
+ UNIX_BOOTSTRAP,
+ UNIX_RAW,
VSOCK,
INET,
};
+
static inline std::string PrintToString(SocketType socketType) {
switch (socketType) {
case SocketType::PRECONNECTED:
return "preconnected_uds";
case SocketType::UNIX:
return "unix_domain_socket";
+ case SocketType::UNIX_BOOTSTRAP:
+ return "unix_domain_socket_bootstrap";
+ case SocketType::UNIX_RAW:
+ return "raw_uds";
case SocketType::VSOCK:
return "vm_socket";
case SocketType::INET:
@@ -86,6 +94,14 @@
}
}
+static inline size_t epochMillis() {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::seconds;
+ using std::chrono::system_clock;
+ return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+}
+
struct BinderRpcOptions {
size_t numThreads = 1;
size_t numSessions = 1;
@@ -167,6 +183,42 @@
return readFd;
}
+// A threadsafe channel where writes block until the value is read.
+template <typename T>
+class HandoffChannel {
+public:
+ void write(T v) {
+ {
+ RpcMutexUniqueLock lock(mMutex);
+ // Wait for space to send.
+ mCvEmpty.wait(lock, [&]() { return !mValue.has_value(); });
+ mValue.emplace(std::move(v));
+ }
+ mCvFull.notify_all();
+ RpcMutexUniqueLock lock(mMutex);
+ // Wait for it to be taken.
+ mCvEmpty.wait(lock, [&]() { return !mValue.has_value(); });
+ }
+
+ T read() {
+ RpcMutexUniqueLock lock(mMutex);
+ if (!mValue.has_value()) {
+ mCvFull.wait(lock, [&]() { return mValue.has_value(); });
+ }
+ T v = std::move(mValue.value());
+ mValue.reset();
+ lock.unlock();
+ mCvEmpty.notify_all();
+ return std::move(v);
+ }
+
+private:
+ RpcMutex mMutex;
+ RpcConditionVariable mCvEmpty;
+ RpcConditionVariable mCvFull;
+ std::optional<T> mValue;
+};
+
using android::binder::Status;
class MyBinderRpcSession : public BnBinderRpcSession {
@@ -374,6 +426,18 @@
out->reset(mockFileDescriptor(acc));
return Status::ok();
}
+
+ HandoffChannel<android::base::unique_fd> mFdChannel;
+
+ Status blockingSendFdOneway(const android::os::ParcelFileDescriptor& fd) override {
+ mFdChannel.write(android::base::unique_fd(fcntl(fd.get(), F_DUPFD_CLOEXEC, 0)));
+ return Status::ok();
+ }
+
+ Status blockingRecvFd(android::os::ParcelFileDescriptor* fd) override {
+ fd->reset(mFdChannel.read());
+ return Status::ok();
+ }
};
} // namespace android
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
new file mode 100644
index 0000000..5a78782
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -0,0 +1,139 @@
+/*
+ * 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
+
+#include <gtest/gtest.h>
+
+#include "binderRpcTestCommon.h"
+
+#define EXPECT_OK(status) \
+ do { \
+ android::binder::Status stat = (status); \
+ EXPECT_TRUE(stat.isOk()) << stat; \
+ } while (false)
+
+namespace android {
+
+// Abstract base class with a virtual destructor that handles the
+// ownership of a process session for BinderRpcTestSession below
+class ProcessSession {
+public:
+ struct SessionInfo {
+ sp<RpcSession> session;
+ sp<IBinder> root;
+ };
+
+ // client session objects associated with other process
+ // each one represents a separate session
+ std::vector<SessionInfo> sessions;
+
+ virtual ~ProcessSession() = 0;
+
+ // If the process exits with a status, run the given callback on that value.
+ virtual void setCustomExitStatusCheck(std::function<void(int wstatus)> f) = 0;
+
+ // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
+ virtual void terminate() = 0;
+};
+
+// Process session where the process hosts IBinderRpcTest, the server used
+// for most testing here
+struct BinderRpcTestProcessSession {
+ std::unique_ptr<ProcessSession> proc;
+
+ // pre-fetched root object (for first session)
+ sp<IBinder> rootBinder;
+
+ // pre-casted root object (for first session)
+ sp<IBinderRpcTest> rootIface;
+
+ // whether session should be invalidated by end of run
+ bool expectAlreadyShutdown = false;
+
+ BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default;
+ ~BinderRpcTestProcessSession() {
+ if (!expectAlreadyShutdown) {
+ EXPECT_NE(nullptr, rootIface);
+ if (rootIface == nullptr) return;
+
+ std::vector<int32_t> remoteCounts;
+ // calling over any sessions counts across all sessions
+ EXPECT_OK(rootIface->countBinders(&remoteCounts));
+ EXPECT_EQ(remoteCounts.size(), proc->sessions.size());
+ for (auto remoteCount : remoteCounts) {
+ EXPECT_EQ(remoteCount, 1);
+ }
+
+ // even though it is on another thread, shutdown races with
+ // the transaction reply being written
+ if (auto status = rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+ }
+
+ rootIface = nullptr;
+ rootBinder = nullptr;
+ }
+};
+
+class BinderRpc : public ::testing::TestWithParam<
+ std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t, bool, bool>> {
+public:
+ SocketType socketType() const { return std::get<0>(GetParam()); }
+ RpcSecurity rpcSecurity() const { return std::get<1>(GetParam()); }
+ uint32_t clientVersion() const { return std::get<2>(GetParam()); }
+ uint32_t serverVersion() const { return std::get<3>(GetParam()); }
+ bool serverSingleThreaded() const { return std::get<4>(GetParam()); }
+ bool noKernel() const { return std::get<5>(GetParam()); }
+
+ bool clientOrServerSingleThreaded() const {
+ return !kEnableRpcThreads || serverSingleThreaded();
+ }
+
+ // Whether the test params support sending FDs in parcels.
+ bool supportsFdTransport() const {
+ return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
+ (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
+ socketType() == SocketType::UNIX_BOOTSTRAP ||
+ socketType() == SocketType::UNIX_RAW);
+ }
+
+ void SetUp() override {
+ if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
+ GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
+ }
+ }
+
+ BinderRpcTestProcessSession createRpcTestSocketServerProcess(const BinderRpcOptions& options) {
+ BinderRpcTestProcessSession ret{
+ .proc = createRpcTestSocketServerProcessEtc(options),
+ };
+
+ ret.rootBinder = ret.proc->sessions.empty() ? nullptr : ret.proc->sessions.at(0).root;
+ ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+ return ret;
+ }
+
+ static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info);
+
+protected:
+ std::unique_ptr<ProcessSession> createRpcTestSocketServerProcessEtc(
+ const BinderRpcOptions& options);
+};
+
+} // namespace android
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 31eb5da..995e761 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -42,6 +42,7 @@
server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
unsigned int outPort = 0;
+ base::unique_fd socketFd(serverConfig.socketFd);
switch (socketType) {
case SocketType::PRECONNECTED:
@@ -50,6 +51,12 @@
CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
<< serverConfig.addr;
break;
+ case SocketType::UNIX_BOOTSTRAP:
+ CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd)));
+ break;
+ case SocketType::UNIX_RAW:
+ CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
+ break;
case SocketType::VSOCK:
CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
break;
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
new file mode 100644
index 0000000..f960442
--- /dev/null
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -0,0 +1,513 @@
+/*
+ * 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 <chrono>
+#include <cstdlib>
+#include <type_traits>
+
+#include "binderRpcTestCommon.h"
+#include "binderRpcTestFixture.h"
+
+using namespace std::chrono_literals;
+using namespace std::placeholders;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+
+static_assert(RPC_WIRE_PROTOCOL_VERSION + 1 == RPC_WIRE_PROTOCOL_VERSION_NEXT ||
+ RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+
+TEST(BinderRpcParcel, EntireParcelFormatted) {
+ Parcel p;
+ p.writeInt32(3);
+
+ EXPECT_DEATH_IF_SUPPORTED(p.markForBinder(sp<BBinder>::make()),
+ "format must be set before data is written");
+}
+
+TEST(BinderRpc, CannotUseNextWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 1));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 2));
+ EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
+}
+
+TEST(BinderRpc, CanUseExperimentalWireVersion) {
+ auto session = RpcSession::make();
+ EXPECT_TRUE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
+}
+
+TEST_P(BinderRpc, Ping) {
+ auto proc = createRpcTestSocketServerProcess({});
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+TEST_P(BinderRpc, GetInterfaceDescriptor) {
+ auto proc = createRpcTestSocketServerProcess({});
+ ASSERT_NE(proc.rootBinder, nullptr);
+ EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor());
+}
+
+TEST_P(BinderRpc, MultipleSessions) {
+ if (serverSingleThreaded()) {
+ // Tests with multiple sessions require a multi-threaded service,
+ // but work fine on a single-threaded client
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 5});
+ for (auto session : proc.proc->sessions) {
+ ASSERT_NE(nullptr, session.root);
+ EXPECT_EQ(OK, session.root->pingBinder());
+ }
+}
+
+TEST_P(BinderRpc, SeparateRootObject) {
+ if (serverSingleThreaded()) {
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ SocketType type = std::get<0>(GetParam());
+ if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
+ type == SocketType::UNIX_BOOTSTRAP || type == SocketType::UNIX_RAW) {
+ // we can't get port numbers for unix sockets
+ return;
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numSessions = 2});
+
+ int port1 = 0;
+ EXPECT_OK(proc.rootIface->getClientPort(&port1));
+
+ sp<IBinderRpcTest> rootIface2 = interface_cast<IBinderRpcTest>(proc.proc->sessions.at(1).root);
+ int port2;
+ EXPECT_OK(rootIface2->getClientPort(&port2));
+
+ // we should have a different IBinderRpcTest object created for each
+ // session, because we use setPerSessionRootObject
+ EXPECT_NE(port1, port2);
+}
+
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
+ auto proc = createRpcTestSocketServerProcess({});
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, AppendSeparateFormats) {
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ Parcel pRaw;
+
+ Parcel p1;
+ p1.markForBinder(proc1.rootBinder);
+ p1.writeInt32(3);
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&pRaw, 0, pRaw.dataSize()));
+ EXPECT_EQ(BAD_TYPE, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
+ Parcel p2;
+ p2.markForBinder(proc2.rootBinder);
+ p2.writeInt32(7);
+
+ EXPECT_EQ(BAD_TYPE, p1.appendFrom(&p2, 0, p2.dataSize()));
+ EXPECT_EQ(BAD_TYPE, p2.appendFrom(&p1, 0, p1.dataSize()));
+}
+
+TEST_P(BinderRpc, UnknownTransaction) {
+ auto proc = createRpcTestSocketServerProcess({});
+ Parcel data;
+ data.markForBinder(proc.rootBinder);
+ Parcel reply;
+ EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
+}
+
+TEST_P(BinderRpc, SendSomethingOneway) {
+ auto proc = createRpcTestSocketServerProcess({});
+ EXPECT_OK(proc.rootIface->sendString("asdf"));
+}
+
+TEST_P(BinderRpc, SendAndGetResultBack) {
+ auto proc = createRpcTestSocketServerProcess({});
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+TEST_P(BinderRpc, SendAndGetResultBackBig) {
+ auto proc = createRpcTestSocketServerProcess({});
+ std::string single = std::string(1024, 'a');
+ std::string doubled;
+ EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
+ EXPECT_EQ(single + single, doubled);
+}
+
+TEST_P(BinderRpc, InvalidNullBinderReturn) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(proc.rootIface->getNullBinder(&outBinder).transactionError(), UNEXPECTED_NULL);
+}
+
+TEST_P(BinderRpc, CallMeBack) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ int32_t pingResult;
+ EXPECT_OK(proc.rootIface->pingMe(new MyBinderRpcSession("foo"), &pingResult));
+ EXPECT_EQ(OK, pingResult);
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST_P(BinderRpc, RepeatBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> inBinder = new MyBinderRpcSession("foo");
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+
+ EXPECT_EQ(0, MyBinderRpcSession::gNum);
+}
+
+TEST_P(BinderRpc, RepeatTheirBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+
+ sp<IBinder> inBinder = IInterface::asBinder(session);
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(inBinder, &outBinder));
+ EXPECT_EQ(inBinder, outBinder);
+
+ wp<IBinder> weak = inBinder;
+ session = nullptr;
+ inBinder = nullptr;
+ outBinder = nullptr;
+
+ // Force reading a reply, to process any pending dec refs from the other
+ // process (the other process will process dec refs there before processing
+ // the ping here).
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST_P(BinderRpc, RepeatBinderNull) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(nullptr, &outBinder));
+ EXPECT_EQ(nullptr, outBinder);
+}
+
+TEST_P(BinderRpc, HoldBinder) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ IBinder* ptr = nullptr;
+ {
+ sp<IBinder> binder = new BBinder();
+ ptr = binder.get();
+ EXPECT_OK(proc.rootIface->holdBinder(binder));
+ }
+
+ sp<IBinder> held;
+ EXPECT_OK(proc.rootIface->getHeldBinder(&held));
+
+ EXPECT_EQ(held.get(), ptr);
+
+ // stop holding binder, because we test to make sure references are cleaned
+ // up
+ EXPECT_OK(proc.rootIface->holdBinder(nullptr));
+ // and flush ref counts
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+}
+
+// START TESTS FOR LIMITATIONS OF SOCKET BINDER
+// These are behavioral differences form regular binder, where certain usecases
+// aren't supported.
+
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
+ auto proc1 = createRpcTestSocketServerProcess({});
+ auto proc2 = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
+}
+
+TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) {
+ if (serverSingleThreaded()) {
+ GTEST_SKIP() << "This test requires a multi-threaded service";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = 1, .numSessions = 2});
+
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(proc.proc->sessions.at(1).root, &outBinder)
+ .transactionError());
+}
+
+TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+ sp<IBinder> outBinder;
+ EXPECT_EQ(INVALID_OPERATION,
+ proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
+}
+
+TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+ if (!kEnableKernelIpc || noKernel()) {
+ GTEST_SKIP() << "Test disabled because Binder kernel driver was disabled "
+ "at build time.";
+ }
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ // for historical reasons, IServiceManager interface only returns the
+ // exception code
+ EXPECT_EQ(binder::Status::EX_TRANSACTION_FAILED,
+ defaultServiceManager()->addService(String16("not_suspicious"), proc.rootBinder));
+}
+
+// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+
+TEST_P(BinderRpc, RepeatRootObject) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> outBinder;
+ EXPECT_OK(proc.rootIface->repeatBinder(proc.rootBinder, &outBinder));
+ EXPECT_EQ(proc.rootBinder, outBinder);
+}
+
+TEST_P(BinderRpc, NestedTransactions) {
+ auto proc = createRpcTestSocketServerProcess({
+ // Enable FD support because it uses more stack space and so represents
+ // something closer to a worst case scenario.
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ auto nastyNester = sp<MyBinderRpcTest>::make();
+ EXPECT_OK(proc.rootIface->nestMe(nastyNester, 10));
+
+ wp<IBinder> weak = nastyNester;
+ nastyNester = nullptr;
+ EXPECT_EQ(nullptr, weak.promote());
+}
+
+TEST_P(BinderRpc, SameBinderEquality) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ EXPECT_EQ(a, b);
+}
+
+TEST_P(BinderRpc, SameBinderEqualityWeak) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ wp<IBinder> weak = a;
+ a = nullptr;
+
+ sp<IBinder> b;
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&b));
+
+ // this is the wrong behavior, since BpBinder
+ // doesn't implement onIncStrongAttempted
+ // but make sure there is no crash
+ EXPECT_EQ(nullptr, weak.promote());
+
+ GTEST_SKIP() << "Weak binders aren't currently re-promotable for RPC binder.";
+
+ // In order to fix this:
+ // - need to have incStrongAttempted reflected across IPC boundary (wait for
+ // response to promote - round trip...)
+ // - sendOnLastWeakRef, to delete entries out of RpcState table
+ EXPECT_EQ(b, weak.promote());
+}
+
+#define expectSessions(expected, iface) \
+ do { \
+ int session; \
+ EXPECT_OK((iface)->getNumOpenSessions(&session)); \
+ EXPECT_EQ(expected, session); \
+ } while (false)
+
+TEST_P(BinderRpc, SingleSession) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession("aoeu", &session));
+ std::string out;
+ EXPECT_OK(session->getName(&out));
+ EXPECT_EQ("aoeu", out);
+
+ expectSessions(1, proc.rootIface);
+ session = nullptr;
+ expectSessions(0, proc.rootIface);
+}
+
+TEST_P(BinderRpc, ManySessions) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ std::vector<sp<IBinderRpcSession>> sessions;
+
+ for (size_t i = 0; i < 15; i++) {
+ expectSessions(i, proc.rootIface);
+ sp<IBinderRpcSession> session;
+ EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
+ sessions.push_back(session);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+ for (size_t i = 0; i < sessions.size(); i++) {
+ std::string out;
+ EXPECT_OK(sessions.at(i)->getName(&out));
+ EXPECT_EQ(std::to_string(i), out);
+ }
+ expectSessions(sessions.size(), proc.rootIface);
+
+ while (!sessions.empty()) {
+ sessions.pop_back();
+ expectSessions(sessions.size(), proc.rootIface);
+ }
+ expectSessions(0, proc.rootIface);
+}
+
+TEST_P(BinderRpc, OnewayCallDoesNotWait) {
+ constexpr size_t kReallyLongTimeMs = 100;
+ constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
+
+ auto proc = createRpcTestSocketServerProcess({});
+
+ size_t epochMsBefore = epochMillis();
+
+ EXPECT_OK(proc.rootIface->sleepMsAsync(kSleepMs));
+
+ size_t epochMsAfter = epochMillis();
+ EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
+}
+
+TEST_P(BinderRpc, Callbacks) {
+ const static std::string kTestString = "good afternoon!";
+
+ for (bool callIsOneway : {true, false}) {
+ for (bool callbackIsOneway : {true, false}) {
+ for (bool delayed : {true, false}) {
+ if (clientOrServerSingleThreaded() &&
+ (callIsOneway || callbackIsOneway || delayed)) {
+ // we have no incoming connections to receive the callback
+ continue;
+ }
+
+ size_t numIncomingConnections = clientOrServerSingleThreaded() ? 0 : 1;
+ auto proc = createRpcTestSocketServerProcess(
+ {.numThreads = 1,
+ .numSessions = 1,
+ .numIncomingConnections = numIncomingConnections});
+ auto cb = sp<MyBinderRpcCallback>::make();
+
+ if (callIsOneway) {
+ EXPECT_OK(proc.rootIface->doCallbackAsync(cb, callbackIsOneway, delayed,
+ kTestString));
+ } else {
+ EXPECT_OK(
+ proc.rootIface->doCallback(cb, callbackIsOneway, delayed, kTestString));
+ }
+
+ // if both transactions are synchronous and the response is sent back on the
+ // same thread, everything should have happened in a nested call. Otherwise,
+ // the callback will be processed on another thread.
+ if (callIsOneway || callbackIsOneway || delayed) {
+ using std::literals::chrono_literals::operator""s;
+ RpcMutexUniqueLock _l(cb->mMutex);
+ cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
+ }
+
+ EXPECT_EQ(cb->mValues.size(), 1)
+ << "callIsOneway: " << callIsOneway
+ << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
+ if (cb->mValues.empty()) continue;
+ EXPECT_EQ(cb->mValues.at(0), kTestString)
+ << "callIsOneway: " << callIsOneway
+ << " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
+
+ // since we are severing the connection, we need to go ahead and
+ // tell the server to shutdown and exit so that waitpid won't hang
+ if (auto status = proc.rootIface->scheduleShutdown(); !status.isOk()) {
+ EXPECT_EQ(DEAD_OBJECT, status.transactionError()) << status;
+ }
+
+ // since this session has an incoming connection w/ a threadpool, we
+ // need to manually shut it down
+ EXPECT_TRUE(proc.proc->sessions.at(0).session->shutdownAndWait(true));
+ proc.expectAlreadyShutdown = true;
+ }
+ }
+ }
+}
+
+TEST_P(BinderRpc, OnewayCallbackWithNoThread) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto cb = sp<MyBinderRpcCallback>::make();
+
+ Status status = proc.rootIface->doCallback(cb, true /*oneway*/, false /*delayed*/, "anything");
+ EXPECT_EQ(WOULD_BLOCK, status.transactionError());
+}
+
+TEST_P(BinderRpc, AidlDelegatorTest) {
+ auto proc = createRpcTestSocketServerProcess({});
+ auto myDelegator = sp<IBinderRpcTestDelegator>::make(proc.rootIface);
+ ASSERT_NE(nullptr, myDelegator);
+
+ std::string doubled;
+ EXPECT_OK(myDelegator->doubleString("cool ", &doubled));
+ EXPECT_EQ("cool cool ", doubled);
+}
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 0210237..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -12,13 +12,17 @@
host_supported: true,
unstable: true,
srcs: [
- "EmptyParcelable.aidl",
- "SingleDataParcelable.aidl",
- "GenericDataParcelable.aidl",
+ "parcelables/EmptyParcelable.aidl",
+ "parcelables/SingleDataParcelable.aidl",
+ "parcelables/GenericDataParcelable.aidl",
],
backend: {
java: {
- enabled: false,
+ enabled: true,
+ platform_apis: true,
+ },
+ rust: {
+ enabled: true,
},
},
}
@@ -84,6 +88,7 @@
},
},
srcs: [
+ "random_binder.cpp",
"random_fd.cpp",
"random_parcel.cpp",
"libbinder_driver.cpp",
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
deleted file mode 100644
index 96d6223..0000000
--- a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
+++ /dev/null
@@ -1,18 +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.
- */
-
-parcelable EmptyParcelable{
-}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 9dac2c9..768fbe1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder"
#include "binder.h"
-#include "EmptyParcelable.h"
-#include "GenericDataParcelable.h"
-#include "SingleDataParcelable.h"
+#include "parcelables/EmptyParcelable.h"
+#include "parcelables/GenericDataParcelable.h"
+#include "parcelables/SingleDataParcelable.h"
#include "util.h"
#include <android-base/hex.h>
@@ -359,19 +359,19 @@
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
- EmptyParcelable emptyParcelable{};
+ parcelables::EmptyParcelable emptyParcelable{};
status_t status = emptyParcelable.readFromParcel(&p);
FUZZ_LOG() << " status: " << status;
},
[] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
- SingleDataParcelable singleDataParcelable;
+ parcelables::SingleDataParcelable singleDataParcelable;
status_t status = singleDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
- GenericDataParcelable genericDataParcelable;
+ parcelables::GenericDataParcelable genericDataParcelable;
status_t status = genericDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index af773a0..53e7de4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder_ndk"
#include "binder_ndk.h"
-#include "aidl/EmptyParcelable.h"
-#include "aidl/GenericDataParcelable.h"
-#include "aidl/SingleDataParcelable.h"
+#include "aidl/parcelables/EmptyParcelable.h"
+#include "aidl/parcelables/GenericDataParcelable.h"
+#include "aidl/parcelables/SingleDataParcelable.h"
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
@@ -183,19 +183,19 @@
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
- aidl::EmptyParcelable emptyParcelable;
+ aidl::parcelables::EmptyParcelable emptyParcelable;
binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
- aidl::SingleDataParcelable singleDataParcelable;
+ aidl::parcelables::SingleDataParcelable singleDataParcelable;
binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
- aidl::GenericDataParcelable genericDataParcelable;
+ aidl::parcelables::GenericDataParcelable genericDataParcelable;
binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
similarity index 69%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
index fc2542b..8fc9263 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h
@@ -14,11 +14,16 @@
* limitations under the License.
*/
-parcelable GenericDataParcelable {
- int data;
- float majorVersion;
- float minorVersion;
- IBinder binder;
- ParcelFileDescriptor fileDescriptor;
- int[] array;
-}
\ No newline at end of file
+#pragma once
+
+#include <binder/IBinder.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+// Get a random binder object for use in fuzzing.
+//
+// May return nullptr.
+sp<IBinder> getRandomBinder(FuzzedDataProvider* provider);
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 25f6096..8bef33f 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -18,6 +18,7 @@
#include <fuzzbinder/random_parcel.h>
#include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
namespace android {
@@ -30,10 +31,19 @@
.extraFds = {},
};
+ if (provider.ConsumeBool()) {
+ // set calling uid
+ IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>());
+ }
+
while (provider.remaining_bytes() > 0) {
- uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ // Most of the AIDL services will have small set of transaction codes.
+ uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+ : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
+ // for increased fuzz coverage
+ data.setEnforceNoDataAvail(provider.ConsumeBool());
sp<IBinder> target = options.extraBinders.at(
provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
@@ -50,6 +60,8 @@
fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
Parcel reply;
+ // for increased fuzz coverage
+ reply.setEnforceNoDataAvail(provider.ConsumeBool());
(void)target->transact(code, data, &reply, flags);
// feed back in binders and fds that are returned from the service, so that
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
index 462ef9a..a1fb701 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
@@ -29,3 +29,12 @@
}
} // namespace android
+
+extern "C" {
+// This API is used by fuzzers to automatically fuzz aidl services
+void fuzzRustService(void* binder, const uint8_t* data, size_t len) {
+ AIBinder* aiBinder = static_cast<AIBinder*>(binder);
+ FuzzedDataProvider provider(data, len);
+ android::fuzzService(aiBinder, std::move(provider));
+}
+} // extern "C"
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
similarity index 92%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
index d62891b..1216250 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-parcelable SingleDataParcelable{
- int data;
+package parcelables;
+parcelable EmptyParcelable {
}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
similarity index 97%
rename from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index fc2542b..f1079e9 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable GenericDataParcelable {
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
index d62891b..0187168 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable SingleDataParcelable{
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/random_binder.cpp b/libs/binder/tests/parcel_fuzzer/random_binder.cpp
new file mode 100644
index 0000000..8a1fecb
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/random_binder.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 <fuzzbinder/random_binder.h>
+
+#include <fuzzbinder/random_parcel.h>
+
+#include <android-base/logging.h>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class RandomBinder : public BBinder {
+public:
+ RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes)
+ : mDescriptor(descriptor),
+ mBytes(std::move(bytes)),
+ mProvider(mBytes.data(), mBytes.size()) {}
+ const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
+ (void)code;
+ (void)data;
+ (void)reply;
+ (void)flags; // note - for maximum coverage even ignore if oneway
+
+ if (mProvider.ConsumeBool()) {
+ return mProvider.ConsumeIntegral<status_t>();
+ }
+
+ if (reply == nullptr) return OK;
+
+ // TODO: things we could do to increase state space
+ // - also pull FDs and binders from 'data'
+ // (optionally combine these into random parcel 'options')
+ // - also pull FDs and binders from random parcel 'options'
+ RandomParcelOptions options;
+
+ // random output
+ std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>(
+ mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes()));
+ fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options);
+
+ return OK;
+ }
+
+private:
+ String16 mDescriptor;
+
+ // note may not all be used
+ std::vector<uint8_t> mBytes;
+ FuzzedDataProvider mProvider;
+};
+
+sp<IBinder> getRandomBinder(FuzzedDataProvider* provider) {
+ auto makeFunc = provider->PickValueInArray<const std::function<sp<IBinder>()>>({
+ [&]() {
+ // descriptor is the length of a class name, e.g.
+ // "some.package.Foo"
+ std::string str = provider->ConsumeRandomLengthString(100 /*max length*/);
+
+ // arbitrarily consume remaining data to create a binder that can return
+ // random results - coverage guided fuzzer should ensure all of the remaining
+ // data isn't always used
+ std::vector<uint8_t> bytes = provider->ConsumeBytes<uint8_t>(
+ provider->ConsumeIntegralInRange<size_t>(0, provider->remaining_bytes()));
+
+ return new RandomBinder(String16(str.c_str()), std::move(bytes));
+ },
+ []() {
+ // this is the easiest remote binder to get ahold of, and it
+ // should be able to handle anything thrown at it, and
+ // essentially every process can talk to it, so it's a good
+ // candidate for checking usage of an actual BpBinder
+ return IInterface::asBinder(defaultServiceManager());
+ },
+ [&]() -> sp<IBinder> { return nullptr; },
+ });
+ return makeFunc();
+}
+
+} // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index 51cb768..f0beed2 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -17,23 +17,14 @@
#include <fuzzbinder/random_parcel.h>
#include <android-base/logging.h>
-#include <binder/IServiceManager.h>
#include <binder/RpcSession.h>
#include <binder/RpcTransportRaw.h>
+#include <fuzzbinder/random_binder.h>
#include <fuzzbinder/random_fd.h>
#include <utils/String16.h>
namespace android {
-class NamedBinder : public BBinder {
-public:
- NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
- const String16& getInterfaceDescriptor() const override { return mDescriptor; }
-
-private:
- String16 mDescriptor;
-};
-
static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(provider.remaining_bytes());
CHECK(OK == p->write(data.data(), data.size()));
@@ -45,6 +36,11 @@
if (provider.ConsumeBool()) {
auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
CHECK_EQ(OK, session->addNullDebuggingClient());
+ // Set the protocol version so that we don't crash if the session
+ // actually gets used. This isn't cheating because the version should
+ // always be set if the session init succeeded and we aren't testing the
+ // session init here (it is bypassed by addNullDebuggingClient).
+ session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION);
p->markForRpc(session);
if (options->writeHeader) {
@@ -77,6 +73,11 @@
1));
CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
} else {
+ // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion
+ if (options->extraFds.size() > 1000) {
+ return;
+ }
+
std::vector<base::unique_fd> fds = getRandomFds(&provider);
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
@@ -89,32 +90,16 @@
},
// write binder
[&]() {
- auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({
- [&]() {
- // descriptor is the length of a class name, e.g.
- // "some.package.Foo"
- std::string str =
- provider.ConsumeRandomLengthString(100 /*max length*/);
- return new NamedBinder(String16(str.c_str()));
- },
- []() {
- // this is the easiest remote binder to get ahold of, and it
- // should be able to handle anything thrown at it, and
- // essentially every process can talk to it, so it's a good
- // candidate for checking usage of an actual BpBinder
- return IInterface::asBinder(defaultServiceManager());
- },
- [&]() -> sp<IBinder> {
- if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
- return options->extraBinders.at(
- provider.ConsumeIntegralInRange<
- size_t>(0, options->extraBinders.size() - 1));
- } else {
- return nullptr;
- }
- },
- });
- sp<IBinder> binder = makeFunc();
+ sp<IBinder> binder;
+ if (options->extraBinders.size() > 0 && provider.ConsumeBool()) {
+ binder = options->extraBinders.at(
+ provider.ConsumeIntegralInRange<size_t>(0,
+ options->extraBinders
+ .size() -
+ 1));
+ } else {
+ binder = getRandomBinder(&provider);
+ }
CHECK(OK == p->writeStrongBinder(binder));
},
});
diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp
new file mode 100644
index 0000000..b48dc27
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/rust_interface/Android.bp
@@ -0,0 +1,24 @@
+package {
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+ name: "libbinder_create_parcel",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: [
+ "RandomParcelWrapper.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "libbinder_random_parcel",
+ ],
+}
diff --git a/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp
new file mode 100644
index 0000000..2fb7820
--- /dev/null
+++ b/libs/binder/tests/parcel_fuzzer/rust_interface/RandomParcelWrapper.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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-base/logging.h>
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <fuzzbinder/random_parcel.h>
+
+extern "C" {
+
+void createRandomParcel(void* aParcel, const uint8_t* data, size_t len) {
+ CHECK_NE(aParcel, nullptr);
+ AParcel* parcel = static_cast<AParcel*>(aParcel);
+ FuzzedDataProvider provider(data, len);
+ android::RandomParcelOptions options;
+
+ android::Parcel* platformParcel = AParcel_viewPlatformParcel(parcel);
+ fillRandomParcel(platformParcel, std::move(provider), &options);
+}
+
+} // extern "C"
\ No newline at end of file
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 46346bb..8ec9823 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -16,6 +16,7 @@
#if defined(TRUSTY_USERSPACE)
#include <openssl/rand.h>
+#include <trusty_ipc.h>
#else
#include <lib/rand/rand.h>
#endif
@@ -23,6 +24,7 @@
#include <binder/RpcTransportTipcTrusty.h>
#include "../OS.h"
+#include "TrustyStatus.h"
using android::base::Result;
@@ -43,13 +45,32 @@
#endif // TRUSTY_USERSPACE
}
-status_t dupFileDescriptor(int /*oldFd*/, int* /*newFd*/) {
- // TODO: implement separately
- return INVALID_OPERATION;
+status_t dupFileDescriptor(int oldFd, int* newFd) {
+ int res = dup(oldFd);
+ if (res < 0) {
+ return statusFromTrusty(res);
+ }
+
+ *newFd = res;
+ return OK;
}
std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() {
return RpcTransportCtxFactoryTipcTrusty::make();
}
+ssize_t sendMessageOnSocket(
+ const RpcTransportFd& /* socket */, iovec* /* iovs */, int /* niovs */,
+ const std::vector<std::variant<base::unique_fd, base::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 */) {
+ errno = ENOTSUP;
+ return -1;
+}
+
} // namespace android
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 0b67b9f..58bfe71 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "RpcTransportTipcTrusty"
+#include <inttypes.h>
#include <trusty_ipc.h>
#include <binder/RpcSession.h>
@@ -47,7 +48,7 @@
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*/)
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds)
override {
if (niovs < 0) {
return BAD_VALUE;
@@ -58,12 +59,32 @@
size += iovs[i].iov_len;
}
+ handle_t msgHandles[IPC_MAX_MSG_HANDLES];
ipc_msg_t msg{
.num_iov = static_cast<uint32_t>(niovs),
.iov = iovs,
- .num_handles = 0, // TODO: add ancillaryFds
+ .num_handles = 0,
.handles = nullptr,
};
+
+ if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+ if (ancillaryFds->size() > IPC_MAX_MSG_HANDLES) {
+ // This shouldn't happen because we check the FD count in RpcState.
+ ALOGE("Saw too many file descriptors in RpcTransportCtxTipcTrusty: "
+ "%zu (max is %u). Aborting session.",
+ ancillaryFds->size(), IPC_MAX_MSG_HANDLES);
+ return BAD_VALUE;
+ }
+
+ for (size_t i = 0; i < ancillaryFds->size(); i++) {
+ msgHandles[i] =
+ std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+ }
+
+ msg.num_handles = ancillaryFds->size();
+ msg.handles = msgHandles;
+ }
+
ssize_t rc = send_msg(mSocket.fd.get(), &msg);
if (rc == ERR_NOT_ENOUGH_BUFFER) {
// Peer is blocked, wait until it unblocks.
@@ -97,8 +118,7 @@
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 {
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override {
if (niovs < 0) {
return BAD_VALUE;
}
@@ -124,11 +144,16 @@
return status;
}
+ LOG_ALWAYS_FATAL_IF(mMessageInfo.num_handles > IPC_MAX_MSG_HANDLES,
+ "Received too many handles %" PRIu32, mMessageInfo.num_handles);
+ bool haveHandles = mMessageInfo.num_handles != 0;
+ handle_t msgHandles[IPC_MAX_MSG_HANDLES];
+
ipc_msg_t msg{
.num_iov = static_cast<uint32_t>(niovs),
.iov = iovs,
- .num_handles = 0, // TODO: support ancillaryFds
- .handles = nullptr,
+ .num_handles = mMessageInfo.num_handles,
+ .handles = haveHandles ? msgHandles : 0,
};
ssize_t rc = read_msg(mSocket.fd.get(), mMessageInfo.id, mMessageOffset, &msg);
if (rc < 0) {
@@ -141,6 +166,28 @@
"Message offset exceeds length %zu/%zu", mMessageOffset,
mMessageInfo.len);
+ if (haveHandles) {
+ 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]));
+ }
+
+ // Clear the saved number of handles so we don't accidentally
+ // read them multiple times
+ mMessageInfo.num_handles = 0;
+ haveHandles = false;
+ } else {
+ ALOGE("Received unexpected handles %" PRIu32, mMessageInfo.num_handles);
+ // It should be safe to continue here. We could abort, but then
+ // peers could DoS us by sending messages with handles in them.
+ // Close the handles since we are ignoring them.
+ for (size_t i = 0; i < mMessageInfo.num_handles; i++) {
+ ::close(msgHandles[i]);
+ }
+ }
+ }
+
// Release the message if all of it has been read
if (mMessageOffset == mMessageInfo.len) {
releaseMessage();
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index cc31c95..7d9dd8c 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -60,6 +60,10 @@
std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+ void setSupportedFileDescriptorTransportModes(
+ const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
+ mRpcServer->setSupportedFileDescriptorTransportModes(modes);
+ }
void setRootObject(const sp<IBinder>& binder) { mRpcServer->setRootObject(binder); }
void setRootObjectWeak(const wp<IBinder>& binder) { mRpcServer->setRootObjectWeak(binder); }
void setPerSessionRootObject(std::function<sp<IBinder>(const void*, size_t)>&& object) {
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index bf877a3..de84617 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -120,3 +120,9 @@
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/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/trusty/ndk/include/sys/cdefs.h
similarity index 73%
copy from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
copy to libs/binder/trusty/ndk/include/sys/cdefs.h
index fc2542b..6a48d2b 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/trusty/ndk/include/sys/cdefs.h
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-parcelable GenericDataParcelable {
- int data;
- float majorVersion;
- float minorVersion;
- IBinder binder;
- ParcelFileDescriptor fileDescriptor;
- int[] array;
-}
\ No newline at end of file
+#include <lk/compiler.h>
+
+/* Alias the bionic macros to the ones from lk/compiler.h */
+#define __BEGIN_DECLS __BEGIN_CDECLS
+#define __END_DECLS __END_CDECLS
+
+#define __INTRODUCED_IN(x) /* nothing on Trusty */
diff --git a/libs/binder/trusty/ndk/rules.mk b/libs/binder/trusty/ndk/rules.mk
new file mode 100644
index 0000000..03fd006
--- /dev/null
+++ b/libs/binder/trusty/ndk/rules.mk
@@ -0,0 +1,38 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+LIBBINDER_NDK_DIR := frameworks/native/libs/binder/ndk
+
+MODULE_SRCS := \
+ $(LIBBINDER_NDK_DIR)/ibinder.cpp \
+ $(LIBBINDER_NDK_DIR)/libbinder.cpp \
+ $(LIBBINDER_NDK_DIR)/parcel.cpp \
+ $(LIBBINDER_NDK_DIR)/status.cpp \
+
+MODULE_EXPORT_INCLUDES += \
+ $(LOCAL_DIR)/include \
+ $(LIBBINDER_NDK_DIR)/include_cpp \
+ $(LIBBINDER_NDK_DIR)/include_ndk \
+ $(LIBBINDER_NDK_DIR)/include_platform \
+
+MODULE_LIBRARY_DEPS += \
+ trusty/user/base/lib/libstdc++-trusty \
+ frameworks/native/libs/binder/trusty \
+
+include make/library.mk
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index a6585c5..067ce17 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -208,5 +208,5 @@
cmdline = std::string(cmdline.c_str());
return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" ||
- cmdline == "usap64";
+ cmdline == "usap64" || cmdline == "webview_zygote";
}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6c6d7f3..1109ad8 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -36,6 +36,9 @@
status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
+ if (service == nullptr) {
+ return UNEXPECTED_NULL;
+ }
mNameToService[name] = service;
return NO_ERROR;
}
@@ -78,6 +81,11 @@
return std::nullopt;
}
+Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) {
+ (void)apexName;
+ return {};
+}
+
std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
const String16& name) {
(void)name;
@@ -98,4 +106,8 @@
std::vector<IServiceManager::ServiceDebugInfo> ret;
return ret;
}
+
+void ServiceManager::clear() {
+ mNameToService.clear();
+}
} // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
index e0af5d4..ba6bb7d 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
@@ -52,6 +52,8 @@
std::optional<String16> updatableViaApex(const String16& name) override;
+ Vector<String16> getUpdatableNames(const String16& apexName) override;
+
std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override;
status_t registerForNotifications(const String16& name,
@@ -62,6 +64,9 @@
std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override;
+ // Clear all of the registered services
+ void clear();
+
private:
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 71e5abe..8682c1c 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -50,6 +50,12 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
+TEST(AddService, SadNullBinder) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
+}
+
TEST(AddService, HappyOverExistingService) {
auto sm = new ServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -58,6 +64,15 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
+TEST(AddService, HappyClearAddedService) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+ EXPECT_NE(sm->getService(String16("foo")), nullptr);
+ sm->clear();
+ EXPECT_EQ(sm->getService(String16("foo")), nullptr);
+}
+
TEST(GetService, HappyHappy) {
auto sm = new ServiceManager();
sp<IBinder> service = getBinder();
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index a25a493..8e57152 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -21,7 +21,11 @@
"fake_guard_test.cpp",
"flags_test.cpp",
"future_test.cpp",
+ "match_test.cpp",
+ "mixins_test.cpp",
+ "non_null_test.cpp",
"optional_test.cpp",
+ "shared_mutex_test.cpp",
"small_map_test.cpp",
"small_vector_test.cpp",
"static_vector_test.cpp",
diff --git a/libs/ftl/concat_test.cpp b/libs/ftl/concat_test.cpp
index 8ecb1b2..771f054 100644
--- a/libs/ftl/concat_test.cpp
+++ b/libs/ftl/concat_test.cpp
@@ -28,8 +28,25 @@
EXPECT_EQ(string.c_str()[string.size()], '\0');
}
+TEST(Concat, Characters) {
+ EXPECT_EQ(ftl::Concat(u'a', ' ', U'b').str(), "97 98");
+}
+
+TEST(Concat, References) {
+ int i[] = {-1, 2};
+ unsigned u = 3;
+ EXPECT_EQ(ftl::Concat(i[0], std::as_const(i[1]), u).str(), "-123");
+
+ const bool b = false;
+ const char c = 'o';
+ EXPECT_EQ(ftl::Concat(b, "tt", c).str(), "falsetto");
+}
+
namespace {
+static_assert(ftl::Concat{true, false, true}.str() == "truefalsetrue");
+static_assert(ftl::Concat{':', '-', ')'}.str() == ":-)");
+
static_assert(ftl::Concat{"foo"}.str() == "foo");
static_assert(ftl::Concat{ftl::truncated<3>("foobar")}.str() == "foo");
diff --git a/libs/ftl/match_test.cpp b/libs/ftl/match_test.cpp
new file mode 100644
index 0000000..a6cff2e
--- /dev/null
+++ b/libs/ftl/match_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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/match.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <string>
+#include <variant>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Match, Example) {
+ using namespace std::chrono;
+ using namespace std::chrono_literals;
+ using namespace std::string_literals;
+
+ std::variant<seconds, minutes, hours> duration = 119min;
+
+ // Mutable match.
+ ftl::match(duration, [](auto& d) { ++d; });
+
+ // Immutable match. Exhaustive due to minutes being convertible to seconds.
+ EXPECT_EQ("2 hours"s,
+ ftl::match(
+ duration,
+ [](const seconds& s) {
+ const auto h = duration_cast<hours>(s);
+ return std::to_string(h.count()) + " hours"s;
+ },
+ [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; }));
+}
+
+} // namespace android::test
diff --git a/libs/ftl/mixins_test.cpp b/libs/ftl/mixins_test.cpp
new file mode 100644
index 0000000..2c9f9df
--- /dev/null
+++ b/libs/ftl/mixins_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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/mixins.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace android::test {
+namespace {
+
+// Keep in sync with example usage in header file.
+
+struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
+ using Constructible::Constructible;
+};
+
+static_assert(!std::is_default_constructible_v<Id>);
+
+struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
+ ftl::Equatable<Color>,
+ ftl::Orderable<Color> {
+ using DefaultConstructible::DefaultConstructible;
+};
+
+static_assert(Color() == Color(0u));
+static_assert(ftl::to_underlying(Color(-1)) == 255u);
+static_assert(Color(1u) < Color(2u));
+
+struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
+ ftl::Equatable<Sequence>,
+ ftl::Orderable<Sequence>,
+ ftl::Incrementable<Sequence> {
+ using DefaultConstructible::DefaultConstructible;
+};
+
+static_assert(Sequence() == Sequence(-1));
+
+struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
+ ftl::Equatable<Timeout>,
+ ftl::Addable<Timeout> {
+ using DefaultConstructible::DefaultConstructible;
+};
+
+using namespace std::chrono_literals;
+static_assert(Timeout() + Timeout(5s) == Timeout(15s));
+
+// Construction.
+constexpr Id kId{1234};
+constexpr Sequence kSequence;
+
+// Underlying value.
+static_assert(ftl::to_underlying(Id(-42)) == -42);
+static_assert(ftl::to_underlying(kSequence) == -1);
+
+// Casting.
+static_assert(static_cast<std::int32_t>(Id(-1)) == -1);
+static_assert(static_cast<std::int8_t>(kSequence) == -1);
+
+static_assert(!std::is_convertible_v<std::int32_t, Id>);
+static_assert(!std::is_convertible_v<Id, std::int32_t>);
+
+// Equality.
+static_assert(kId == Id(1234));
+static_assert(kId != Id(123));
+static_assert(kSequence == Sequence(-1));
+
+// Ordering.
+static_assert(Sequence(1) < Sequence(2));
+static_assert(Sequence(2) > Sequence(1));
+static_assert(Sequence(3) <= Sequence(4));
+static_assert(Sequence(4) >= Sequence(3));
+static_assert(Sequence(5) <= Sequence(5));
+static_assert(Sequence(6) >= Sequence(6));
+
+// Incrementing.
+template <typename Op, typename T, typename... Ts>
+constexpr auto mutable_op(Op op, T lhs, Ts... rhs) {
+ const T result = op(lhs, rhs...);
+ return std::make_pair(lhs, result);
+}
+
+static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Sequence()) ==
+ std::make_pair(Sequence(0), Sequence(0)));
+
+static_assert(mutable_op([](auto& lhs) { return lhs++; }, Sequence()) ==
+ std::make_pair(Sequence(0), Sequence(-1)));
+
+// Addition.
+
+// `Addable` implies `Incrementable`.
+static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Timeout()) ==
+ std::make_pair(Timeout(11s), Timeout(11s)));
+
+static_assert(mutable_op([](auto& lhs) { return lhs++; }, Timeout()) ==
+ std::make_pair(Timeout(11s), Timeout(10s)));
+
+static_assert(Timeout(5s) + Timeout(6s) == Timeout(11s));
+
+static_assert(mutable_op([](auto& lhs, const auto& rhs) { return lhs += rhs; }, Timeout(7s),
+ Timeout(8s)) == std::make_pair(Timeout(15s), Timeout(15s)));
+
+// Type safety.
+
+namespace traits {
+
+template <typename, typename = void>
+struct is_incrementable : std::false_type {};
+
+template <typename T>
+struct is_incrementable<T, std::void_t<decltype(++std::declval<T&>())>> : std::true_type {};
+
+template <typename T>
+constexpr bool is_incrementable_v = is_incrementable<T>{};
+
+template <typename, typename, typename, typename = void>
+struct has_binary_op : std::false_type {};
+
+template <typename Op, typename T, typename U>
+struct has_binary_op<Op, T, U, std::void_t<decltype(Op{}(std::declval<T&>(), std::declval<U&>()))>>
+ : std::true_type {};
+
+template <typename T, typename U>
+constexpr bool is_equatable_v =
+ has_binary_op<std::equal_to<void>, T, U>{} && has_binary_op<std::not_equal_to<void>, T, U>{};
+
+template <typename T, typename U>
+constexpr bool is_orderable_v =
+ has_binary_op<std::less<void>, T, U>{} && has_binary_op<std::less_equal<void>, T, U>{} &&
+ has_binary_op<std::greater<void>, T, U>{} && has_binary_op<std::greater_equal<void>, T, U>{};
+
+template <typename T, typename U>
+constexpr bool is_addable_v = has_binary_op<std::plus<void>, T, U>{};
+
+} // namespace traits
+
+struct Real : ftl::Constructible<Real, float> {
+ using Constructible::Constructible;
+};
+
+static_assert(traits::is_equatable_v<Id, Id>);
+static_assert(!traits::is_equatable_v<Real, Real>);
+static_assert(!traits::is_equatable_v<Id, Color>);
+static_assert(!traits::is_equatable_v<Sequence, Id>);
+static_assert(!traits::is_equatable_v<Id, std::int32_t>);
+static_assert(!traits::is_equatable_v<std::chrono::seconds, Timeout>);
+
+static_assert(traits::is_orderable_v<Color, Color>);
+static_assert(!traits::is_orderable_v<Id, Id>);
+static_assert(!traits::is_orderable_v<Real, Real>);
+static_assert(!traits::is_orderable_v<Color, Sequence>);
+static_assert(!traits::is_orderable_v<Color, std::uint8_t>);
+static_assert(!traits::is_orderable_v<std::chrono::seconds, Timeout>);
+
+static_assert(traits::is_incrementable_v<Sequence>);
+static_assert(traits::is_incrementable_v<Timeout>);
+static_assert(!traits::is_incrementable_v<Id>);
+static_assert(!traits::is_incrementable_v<Color>);
+static_assert(!traits::is_incrementable_v<Real>);
+
+static_assert(traits::is_addable_v<Timeout, Timeout>);
+static_assert(!traits::is_addable_v<Id, Id>);
+static_assert(!traits::is_addable_v<Real, Real>);
+static_assert(!traits::is_addable_v<Sequence, Sequence>);
+static_assert(!traits::is_addable_v<Timeout, Sequence>);
+static_assert(!traits::is_addable_v<Color, Timeout>);
+
+} // namespace
+} // namespace android::test
diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp
new file mode 100644
index 0000000..bd0462b
--- /dev/null
+++ b/libs/ftl/non_null_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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/non_null.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+namespace android::test {
+namespace {
+
+void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr,
+ ftl::NonNull<std::size_t*> length_ptr) {
+ // No need for `nullptr` checks.
+ *length_ptr = string_ptr->length();
+}
+
+using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>;
+
+Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) {
+ // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point.
+ auto unique_ptr = std::move(non_null_ptr).take();
+
+ auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr)));
+ auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr;
+
+ return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)};
+}
+
+} // namespace
+
+// Keep in sync with example usage in header file.
+TEST(NonNull, Example) {
+ const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
+ std::size_t size;
+ get_length(string_ptr, ftl::as_non_null(&size));
+ EXPECT_EQ(size, 7u);
+
+ auto ptr = ftl::as_non_null(std::make_unique<int>(42));
+ const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true);
+ EXPECT_EQ(ptr1.get(), ptr2);
+}
+
+namespace {
+
+constexpr std::string_view kApple = "apple";
+constexpr std::string_view kOrange = "orange";
+
+using StringViewPtr = ftl::NonNull<const std::string_view*>;
+constexpr StringViewPtr kApplePtr = ftl::as_non_null(&kApple);
+constexpr StringViewPtr kOrangePtr = ftl::as_non_null(&kOrange);
+
+constexpr StringViewPtr longest(StringViewPtr ptr1, StringViewPtr ptr2) {
+ return ptr1->length() > ptr2->length() ? ptr1 : ptr2;
+}
+
+static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr);
+
+} // namespace
+} // namespace android::test
diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
index f7410c2..6b3b6c4 100644
--- a/libs/ftl/optional_test.cpp
+++ b/libs/ftl/optional_test.cpp
@@ -164,4 +164,35 @@
}));
}
+// Comparison.
+namespace {
+
+constexpr Optional<int> kOptional1 = 1;
+constexpr Optional<int> kAnotherOptional1 = 1;
+constexpr Optional<int> kOptional2 = 2;
+constexpr Optional<int> kOptionalEmpty, kAnotherOptionalEmpty;
+
+constexpr std::optional<int> kStdOptional1 = 1;
+
+static_assert(kOptional1 == kAnotherOptional1);
+
+static_assert(kOptional1 != kOptional2);
+static_assert(kOptional2 != kOptional1);
+
+static_assert(kOptional1 != kOptionalEmpty);
+static_assert(kOptionalEmpty != kOptional1);
+
+static_assert(kOptionalEmpty == kAnotherOptionalEmpty);
+
+static_assert(kOptional1 == kStdOptional1);
+static_assert(kStdOptional1 == kOptional1);
+
+static_assert(kOptional2 != kStdOptional1);
+static_assert(kStdOptional1 != kOptional2);
+
+static_assert(kOptional2 != kOptionalEmpty);
+static_assert(kOptionalEmpty != kOptional2);
+
+} // namespace
+
} // namespace android::test
diff --git a/libs/ftl/shared_mutex_test.cpp b/libs/ftl/shared_mutex_test.cpp
new file mode 100644
index 0000000..6da7061
--- /dev/null
+++ b/libs/ftl/shared_mutex_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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/shared_mutex.h>
+#include <gtest/gtest.h>
+#include <ftl/fake_guard.h>
+
+namespace android::test {
+
+TEST(SharedMutex, SharedLock) {
+ ftl::SharedMutex mutex;
+ std::shared_lock shared_lock(mutex);
+
+ { std::shared_lock shared_lock2(mutex); }
+}
+
+TEST(SharedMutex, ExclusiveLock) {
+ ftl::SharedMutex mutex;
+ std::unique_lock unique_lock(mutex);
+}
+
+TEST(SharedMutex, Annotations) {
+ struct {
+ void foo() FTL_ATTRIBUTE(requires_shared_capability(mutex)) { num++; }
+ void bar() FTL_ATTRIBUTE(requires_capability(mutex)) { num++; }
+ void baz() {
+ std::shared_lock shared_lock(mutex);
+ num++;
+ }
+ ftl::SharedMutex mutex;
+ int num = 0;
+
+ } s;
+
+ {
+ // TODO(b/257958323): Use an RAII class instead of locking manually.
+ s.mutex.lock_shared();
+ s.foo();
+ s.baz();
+ s.mutex.unlock_shared();
+ }
+ s.mutex.lock();
+ s.bar();
+ s.mutex.unlock();
+}
+
+} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 3d81c32..6d1dfe8 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -58,7 +58,7 @@
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.common-V4-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index a96a07a..af50a29 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -27,10 +27,13 @@
srcs: [
"GpuStatsInfo.cpp",
"GraphicsEnv.cpp",
- "IGpuService.cpp"
+ "IGpuService.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
@@ -46,4 +49,13 @@
],
export_include_dirs: ["include"],
+
+ product_variables: {
+ // `debuggable` is set for eng and userdebug builds
+ debuggable: {
+ cflags: [
+ "-DANDROID_DEBUGGABLE",
+ ],
+ },
+ },
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 4a0a839..5f5f85a 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -126,7 +126,20 @@
}
bool GraphicsEnv::isDebuggable() {
- return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
+ // This flag determines if the application is marked debuggable
+ bool appDebuggable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
+
+ // This flag is set only in `debuggable` builds of the platform
+#if defined(ANDROID_DEBUGGABLE)
+ bool platformDebuggable = true;
+#else
+ bool platformDebuggable = false;
+#endif
+
+ ALOGV("GraphicsEnv::isDebuggable returning appDebuggable=%s || platformDebuggable=%s",
+ appDebuggable ? "true" : "false", platformDebuggable ? "true" : "false");
+
+ return appDebuggable || platformDebuggable;
}
void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 82a6b6c..73d3196 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -35,7 +35,7 @@
// Check if the process is debuggable. It returns false except in any of the
// following circumstances:
- // 1. ro.debuggable=1 (global debuggable enabled).
+ // 1. ANDROID_DEBUGGABLE is defined (global debuggable enabled).
// 2. android:debuggable="true" in the manifest for an individual app.
// 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
// 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d3144bb..a988e39 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -191,8 +191,6 @@
"BitTube.cpp",
"BLASTBufferQueue.cpp",
- "BufferHubConsumer.cpp",
- "BufferHubProducer.cpp",
"BufferItemConsumer.cpp",
"CompositorTiming.cpp",
"ConsumerBase.cpp",
@@ -217,7 +215,6 @@
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
- "TransactionTracing.cpp",
"VsyncEventData.cpp",
"view/Surface.cpp",
"WindowInfosListenerReporter.cpp",
@@ -229,9 +226,6 @@
shared_libs: [
"libbinder",
- "libbufferhub",
- "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
- "libpdx_default_transport",
],
export_shared_lib_headers: [
@@ -242,24 +236,6 @@
"libgui_aidl_headers",
],
- // bufferhub is not used when building libgui for vendors
- target: {
- vendor: {
- cflags: [
- "-DNO_BUFFERHUB",
- ],
- exclude_srcs: [
- "BufferHubConsumer.cpp",
- "BufferHubProducer.cpp",
- ],
- exclude_shared_libs: [
- "libbufferhub",
- "libbufferhubqueue",
- "libpdx_default_transport",
- ],
- },
- },
-
aidl: {
export_aidl_headers: true,
},
@@ -289,7 +265,6 @@
min_sdk_version: "29",
cflags: [
- "-DNO_BUFFERHUB",
"-DNO_BINDER",
],
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3afa339..97e45c6 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -167,14 +167,15 @@
mNumFrameAvailable = 0;
TransactionCompletedListener::getInstance()->addQueueStallListener(
- [&]() {
- std::function<void(bool)> callbackCopy;
- {
- std::unique_lock _lock{mMutex};
- callbackCopy = mTransactionHangCallback;
- }
- if (callbackCopy) callbackCopy(true);
- }, this);
+ [&](const std::string& reason) {
+ std::function<void(const std::string&)> callbackCopy;
+ {
+ std::unique_lock _lock{mMutex};
+ callbackCopy = mTransactionHangCallback;
+ }
+ if (callbackCopy) callbackCopy(reason);
+ },
+ this);
BQA_LOGV("BLASTBufferQueue created");
}
@@ -307,7 +308,6 @@
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
"empty.");
}
-
decStrong((void*)transactionCommittedCallbackThunk);
}
}
@@ -335,9 +335,11 @@
std::optional<SurfaceControlStats> statsOptional = findMatchingStat(stats, pendingSC);
if (statsOptional) {
SurfaceControlStats stat = *statsOptional;
- mTransformHint = stat.transformHint;
- mBufferItemConsumer->setTransformHint(mTransformHint);
- BQA_LOGV("updated mTransformHint=%d", mTransformHint);
+ if (stat.transformHint) {
+ mTransformHint = *stat.transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
+ BQA_LOGV("updated mTransformHint=%d", mTransformHint);
+ }
// Update frametime stamps if the frame was latched and presented, indicated by a
// valid latch time.
if (stat.latchTime > 0) {
@@ -350,6 +352,21 @@
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
+ auto currFrameNumber = stat.frameEventStats.frameNumber;
+ std::vector<ReleaseCallbackId> staleReleases;
+ for (const auto& [key, value]: mSubmitted) {
+ if (currFrameNumber > key.framenumber) {
+ staleReleases.push_back(key);
+ }
+ }
+ for (const auto& staleRelease : staleReleases) {
+ releaseBufferCallbackLocked(staleRelease,
+ stat.previousReleaseFence
+ ? stat.previousReleaseFence
+ : Fence::NO_FENCE,
+ stat.currentMaxAcquiredBufferCount,
+ true /* fakeRelease */);
+ }
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -390,7 +407,16 @@
const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount) {
BBQ_TRACE();
+
std::unique_lock _lock{mMutex};
+ releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount,
+ false /* fakeRelease */);
+}
+
+void BLASTBufferQueue::releaseBufferCallbackLocked(
+ const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) {
+ ATRACE_CALL();
BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
// Calculate how many buffers we need to hold before we release them back
@@ -408,7 +434,16 @@
const auto numPendingBuffersToHold =
isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
- mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
+
+ auto rb = ReleasedBuffer{id, releaseFence};
+ if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) {
+ mPendingRelease.emplace_back(rb);
+ if (fakeRelease) {
+ BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback %" PRIu64,
+ id.framenumber);
+ BBQ_TRACE("FakeReleaseCallback");
+ }
+ }
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
@@ -1088,7 +1123,8 @@
return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
}
-void BLASTBufferQueue::setTransactionHangCallback(std::function<void(bool)> callback) {
+void BLASTBufferQueue::setTransactionHangCallback(
+ std::function<void(const std::string&)> callback) {
std::unique_lock _lock{mMutex};
mTransactionHangCallback = callback;
}
diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp
deleted file mode 100644
index b5cdeb2..0000000
--- a/libs/gui/BufferHubConsumer.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2018 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 <gui/BufferHubConsumer.h>
-
-namespace android {
-
-using namespace dvr;
-
-/* static */
-sp<BufferHubConsumer> BufferHubConsumer::Create(const std::shared_ptr<ConsumerQueue>& queue) {
- sp<BufferHubConsumer> consumer = new BufferHubConsumer;
- consumer->mQueue = queue;
- return consumer;
-}
-
-/* static */ sp<BufferHubConsumer> BufferHubConsumer::Create(ConsumerQueueParcelable parcelable) {
- if (!parcelable.IsValid()) {
- ALOGE("BufferHubConsumer::Create: Invalid consumer parcelable.");
- return nullptr;
- }
-
- sp<BufferHubConsumer> consumer = new BufferHubConsumer;
- consumer->mQueue = ConsumerQueue::Import(parcelable.TakeChannelHandle());
- return consumer;
-}
-
-status_t BufferHubConsumer::acquireBuffer(BufferItem* /*buffer*/, nsecs_t /*presentWhen*/,
- uint64_t /*maxFrameNumber*/) {
- ALOGE("BufferHubConsumer::acquireBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::detachBuffer(int /*slot*/) {
- ALOGE("BufferHubConsumer::detachBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::attachBuffer(int* /*outSlot*/, const sp<GraphicBuffer>& /*buffer*/) {
- ALOGE("BufferHubConsumer::attachBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::releaseBuffer(int /*buf*/, uint64_t /*frameNumber*/,
- EGLDisplay /*display*/, EGLSyncKHR /*fence*/,
- const sp<Fence>& /*releaseFence*/) {
- ALOGE("BufferHubConsumer::releaseBuffer: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::consumerConnect(const sp<IConsumerListener>& /*consumer*/,
- bool /*controlledByApp*/) {
- ALOGE("BufferHubConsumer::consumerConnect: not implemented.");
-
- // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to
- // make IGraphicBufferConsumer_test happy.
- return NO_ERROR;
-}
-
-status_t BufferHubConsumer::consumerDisconnect() {
- ALOGE("BufferHubConsumer::consumerDisconnect: not implemented.");
-
- // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to
- // make IGraphicBufferConsumer_test happy.
- return NO_ERROR;
-}
-
-status_t BufferHubConsumer::getReleasedBuffers(uint64_t* /*slotMask*/) {
- ALOGE("BufferHubConsumer::getReleasedBuffers: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {
- ALOGE("BufferHubConsumer::setDefaultBufferSize: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setMaxBufferCount(int /*bufferCount*/) {
- ALOGE("BufferHubConsumer::setMaxBufferCount: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setMaxAcquiredBufferCount(int /*maxAcquiredBuffers*/) {
- ALOGE("BufferHubConsumer::setMaxAcquiredBufferCount: not implemented.");
-
- // TODO(b/73267953): Make BufferHub honor producer and consumer connection. Returns NO_ERROR to
- // make IGraphicBufferConsumer_test happy.
- return NO_ERROR;
-}
-
-status_t BufferHubConsumer::setConsumerName(const String8& /*name*/) {
- ALOGE("BufferHubConsumer::setConsumerName: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setDefaultBufferFormat(PixelFormat /*defaultFormat*/) {
- ALOGE("BufferHubConsumer::setDefaultBufferFormat: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setDefaultBufferDataSpace(android_dataspace /*defaultDataSpace*/) {
- ALOGE("BufferHubConsumer::setDefaultBufferDataSpace: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setConsumerUsageBits(uint64_t /*usage*/) {
- ALOGE("BufferHubConsumer::setConsumerUsageBits: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setConsumerIsProtected(bool /*isProtected*/) {
- ALOGE("BufferHubConsumer::setConsumerIsProtected: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::setTransformHint(uint32_t /*hint*/) {
- ALOGE("BufferHubConsumer::setTransformHint: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::getSidebandStream(sp<NativeHandle>* /*outStream*/) const {
- ALOGE("BufferHubConsumer::getSidebandStream: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::getOccupancyHistory(
- bool /*forceFlush*/, std::vector<OccupancyTracker::Segment>* /*outHistory*/) {
- ALOGE("BufferHubConsumer::getOccupancyHistory: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::discardFreeBuffers() {
- ALOGE("BufferHubConsumer::discardFreeBuffers: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const {
- ALOGE("BufferHubConsumer::dumpState: not implemented.");
- return INVALID_OPERATION;
-}
-
-IBinder* BufferHubConsumer::onAsBinder() {
- ALOGE("BufferHubConsumer::onAsBinder: BufferHubConsumer should never be used as an Binder "
- "object.");
- return nullptr;
-}
-
-} // namespace android
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
deleted file mode 100644
index 1f71e23..0000000
--- a/libs/gui/BufferHubProducer.cpp
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * Copyright 2018 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 <dvr/dvr_api.h>
-#include <gui/BufferHubProducer.h>
-#include <inttypes.h>
-#include <log/log.h>
-#include <system/window.h>
-
-namespace android {
-
-using namespace dvr;
-
-/* static */
-sp<BufferHubProducer> BufferHubProducer::Create(const std::shared_ptr<ProducerQueue>& queue) {
- sp<BufferHubProducer> producer = new BufferHubProducer;
- producer->queue_ = queue;
- return producer;
-}
-
-/* static */
-sp<BufferHubProducer> BufferHubProducer::Create(ProducerQueueParcelable parcelable) {
- if (!parcelable.IsValid()) {
- ALOGE("BufferHubProducer::Create: Invalid producer parcelable.");
- return nullptr;
- }
-
- sp<BufferHubProducer> producer = new BufferHubProducer;
- producer->queue_ = ProducerQueue::Import(parcelable.TakeChannelHandle());
- return producer;
-}
-
-status_t BufferHubProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- ALOGV("requestBuffer: slot=%d", slot);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("requestBuffer: BufferHubProducer has no connected producer");
- return NO_INIT;
- }
-
- if (slot < 0 || slot >= max_buffer_count_) {
- ALOGE("requestBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("requestBuffer: slot %d is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (buffers_[slot].mGraphicBuffer != nullptr) {
- ALOGE("requestBuffer: slot %d is not empty.", slot);
- return BAD_VALUE;
- } else if (buffers_[slot].mProducerBuffer == nullptr) {
- ALOGE("requestBuffer: slot %d is not dequeued.", slot);
- return BAD_VALUE;
- }
-
- const auto& producer_buffer = buffers_[slot].mProducerBuffer;
- sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
-
- buffers_[slot].mGraphicBuffer = graphic_buffer;
- buffers_[slot].mRequestBufferCalled = true;
-
- *buf = graphic_buffer;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setMaxDequeuedBufferCount(int max_dequeued_buffers) {
- ALOGV("setMaxDequeuedBufferCount: max_dequeued_buffers=%d", max_dequeued_buffers);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (max_dequeued_buffers <= 0 ||
- max_dequeued_buffers >
- int(BufferHubQueue::kMaxQueueCapacity - kDefaultUndequeuedBuffers)) {
- ALOGE("setMaxDequeuedBufferCount: %d out of range (0, %zu]", max_dequeued_buffers,
- BufferHubQueue::kMaxQueueCapacity);
- return BAD_VALUE;
- }
-
- // The new dequeued_buffers count should not be violated by the number
- // of currently dequeued buffers.
- int dequeued_count = 0;
- for (const auto& buf : buffers_) {
- if (buf.mBufferState.isDequeued()) {
- dequeued_count++;
- }
- }
- if (dequeued_count > max_dequeued_buffers) {
- ALOGE("setMaxDequeuedBufferCount: the requested dequeued_buffers"
- "count (%d) exceeds the current dequeued buffer count (%d)",
- max_dequeued_buffers, dequeued_count);
- return BAD_VALUE;
- }
-
- max_dequeued_buffer_count_ = max_dequeued_buffers;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setAsyncMode(bool async) {
- if (async) {
- // TODO(b/36724099) BufferHubQueue's consumer end always acquires the buffer
- // automatically and behaves differently from IGraphicBufferConsumer. Thus,
- // android::BufferQueue's async mode (a.k.a. allocating an additional buffer
- // to prevent dequeueBuffer from being blocking) technically does not apply
- // here.
- //
- // In Daydream, non-blocking producer side dequeue is guaranteed by careful
- // buffer consumer implementations. In another word, BufferHubQueue based
- // dequeueBuffer should never block whether setAsyncMode(true) is set or
- // not.
- //
- // See: IGraphicBufferProducer::setAsyncMode and
- // BufferQueueProducer::setAsyncMode for more about original implementation.
- ALOGW("BufferHubProducer::setAsyncMode: BufferHubQueue should always be "
- "asynchronous. This call makes no effact.");
- return NO_ERROR;
- }
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width,
- uint32_t height, PixelFormat format, uint64_t usage,
- uint64_t* /*outBufferAge*/,
- FrameEventHistoryDelta* /* out_timestamps */) {
- ALOGV("dequeueBuffer: w=%u, h=%u, format=%d, usage=%" PRIu64, width, height, format, usage);
-
- status_t ret;
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("dequeueBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- const uint32_t kLayerCount = 1;
- if (int32_t(queue_->capacity()) < max_dequeued_buffer_count_ + kDefaultUndequeuedBuffers) {
- // Lazy allocation. When the capacity of |queue_| has not reached
- // |max_dequeued_buffer_count_|, allocate new buffer.
- // TODO(jwcai) To save memory, the really reasonable thing to do is to go
- // over existing slots and find first existing one to dequeue.
- ret = AllocateBuffer(width, height, kLayerCount, format, usage);
- if (ret < 0) return ret;
- }
-
- size_t slot = 0;
- std::shared_ptr<ProducerBuffer> producer_buffer;
-
- for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) {
- LocalHandle fence;
- auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence);
- if (!buffer_status) return NO_MEMORY;
-
- producer_buffer = buffer_status.take();
- if (!producer_buffer) return NO_MEMORY;
-
- if (width == producer_buffer->width() && height == producer_buffer->height() &&
- uint32_t(format) == producer_buffer->format()) {
- // The producer queue returns a producer buffer matches the request.
- break;
- }
-
- // Needs reallocation.
- // TODO(jwcai) Consider use VLOG instead if we find this log is not useful.
- ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different "
- "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need "
- "re-allocattion.",
- width, height, format, slot, producer_buffer->width(), producer_buffer->height(),
- producer_buffer->format());
- // Mark the slot as reallocating, so that later we can set
- // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued.
- buffers_[slot].mIsReallocating = true;
-
- // Remove the old buffer once the allocation before allocating its
- // replacement.
- RemoveBuffer(slot);
-
- // Allocate a new producer buffer with new buffer configs. Note that if
- // there are already multiple buffers in the queue, the next one returned
- // from |queue_->Dequeue| may not be the new buffer we just reallocated.
- // Retry up to BufferHubQueue::kMaxQueueCapacity times.
- ret = AllocateBuffer(width, height, kLayerCount, format, usage);
- if (ret < 0) return ret;
- }
-
- // With the BufferHub backed solution. Buffer slot returned from
- // |queue_->Dequeue| is guaranteed to avaiable for producer's use.
- // It's either in free state (if the buffer has never been used before) or
- // in queued state (if the buffer has been dequeued and queued back to
- // BufferHubQueue).
- LOG_ALWAYS_FATAL_IF((!buffers_[slot].mBufferState.isFree() &&
- !buffers_[slot].mBufferState.isQueued()),
- "dequeueBuffer: slot %zu is not free or queued, actual state: %s.", slot,
- buffers_[slot].mBufferState.string());
-
- buffers_[slot].mBufferState.freeQueued();
- buffers_[slot].mBufferState.dequeue();
- ALOGV("dequeueBuffer: slot=%zu", slot);
-
- // TODO(jwcai) Handle fence properly. |BufferHub| has full fence support, we
- // just need to exopose that through |BufferHubQueue| once we need fence.
- *out_fence = Fence::NO_FENCE;
- *out_slot = int(slot);
- ret = NO_ERROR;
-
- if (buffers_[slot].mIsReallocating) {
- ret |= BUFFER_NEEDS_REALLOCATION;
- buffers_[slot].mIsReallocating = false;
- }
-
- return ret;
-}
-
-status_t BufferHubProducer::detachBuffer(int slot) {
- ALOGV("detachBuffer: slot=%d", slot);
- std::unique_lock<std::mutex> lock(mutex_);
-
- return DetachBufferLocked(static_cast<size_t>(slot));
-}
-
-status_t BufferHubProducer::DetachBufferLocked(size_t slot) {
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("detachBuffer: BufferHubProducer is not connected.");
- return NO_INIT;
- }
-
- if (slot >= static_cast<size_t>(max_buffer_count_)) {
- ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (!buffers_[slot].mRequestBufferCalled) {
- ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot);
- return BAD_VALUE;
- }
- std::shared_ptr<ProducerBuffer> producer_buffer = queue_->GetBuffer(slot);
- if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) {
- ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot);
- return BAD_VALUE;
- }
- sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
- if (graphic_buffer == nullptr) {
- ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot);
- return BAD_VALUE;
- }
-
- // Remove the ProducerBuffer from the ProducerQueue.
- status_t error = RemoveBuffer(slot);
- if (error != NO_ERROR) {
- ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error);
- return error;
- }
-
- // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject
- // the handle into the GraphicBuffer object at the requested slot.
- auto status_or_handle = producer_buffer->Detach();
- if (!status_or_handle.ok()) {
- ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot,
- status_or_handle.error());
- return BAD_VALUE;
- }
-
- // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
- // be directly backed by BufferHub.
- return INVALID_OPERATION;
-}
-
-status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) {
- ALOGV("detachNextBuffer.");
-
- if (out_buffer == nullptr || out_fence == nullptr) {
- ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer,
- out_fence);
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("detachNextBuffer: BufferHubProducer is not connected.");
- return NO_INIT;
- }
-
- // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in
- // sequence, except for two things:
- //
- // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the
- // function just returns whatever ProducerBuffer is available from the ProducerQueue and no
- // buffer allocation or re-allocation will happen.
- // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return
- // an error instead.
- size_t slot = 0;
- LocalHandle fence;
-
- // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error
- // immediately if ProducerQueue::Dequeue() fails.
- auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence);
- if (!status_or_buffer.ok()) {
- ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error());
- return NO_MEMORY;
- }
-
- std::shared_ptr<ProducerBuffer> producer_buffer = status_or_buffer.take();
- if (producer_buffer == nullptr) {
- ALOGE("detachNextBuffer: Dequeued buffer is null.");
- return NO_MEMORY;
- }
-
- // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to
- // be available for producer's use. It's either in free state (if the buffer has never been used
- // before) or in queued state (if the buffer has been dequeued and queued back to
- // BufferHubQueue).
- if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) {
- ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- }
- if (buffers_[slot].mProducerBuffer == nullptr) {
- ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot);
- return BAD_VALUE;
- }
- if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) {
- ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: "
- "%d, expected: %d.",
- slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id());
- return BAD_VALUE;
- }
-
- ALOGV("detachNextBuffer: slot=%zu", slot);
- buffers_[slot].mBufferState.freeQueued();
- buffers_[slot].mBufferState.dequeue();
-
- // Second, request the buffer.
- sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer();
- buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer();
-
- // Finally, detach the buffer and then return.
- status_t error = DetachBufferLocked(slot);
- if (error == NO_ERROR) {
- *out_fence = new Fence(fence.Release());
- *out_buffer = graphic_buffer;
- }
- return error;
-}
-
-status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) {
- // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only
- // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer.
- ALOGV("queueBuffer: buffer=%p", buffer.get());
-
- if (out_slot == nullptr) {
- ALOGE("attachBuffer: out_slot cannot be NULL.");
- return BAD_VALUE;
- }
- if (buffer == nullptr) {
- ALOGE("attachBuffer: invalid GraphicBuffer.");
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("attachBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- // Before attaching the buffer, caller is supposed to call
- // IGraphicBufferProducer::setGenerationNumber to inform the
- // BufferHubProducer the next generation number.
- if (buffer->getGenerationNumber() != generation_number_) {
- ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.",
- buffer->getGenerationNumber(), generation_number_);
- return BAD_VALUE;
- }
-
- // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can
- // be directly backed by BufferHub.
- return INVALID_OPERATION;
-}
-
-status_t BufferHubProducer::queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output) {
- ALOGV("queueBuffer: slot %d", slot);
-
- if (output == nullptr) {
- return BAD_VALUE;
- }
-
- int64_t timestamp;
- bool is_auto_timestamp;
- android_dataspace dataspace;
- Rect crop(Rect::EMPTY_RECT);
- int scaling_mode;
- uint32_t transform;
- sp<Fence> fence;
-
- input.deflate(×tamp, &is_auto_timestamp, &dataspace, &crop, &scaling_mode, &transform,
- &fence);
-
- // Check input scaling mode is valid.
- switch (scaling_mode) {
- case NATIVE_WINDOW_SCALING_MODE_FREEZE:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
- case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
- break;
- default:
- ALOGE("queueBuffer: unknown scaling mode %d", scaling_mode);
- return BAD_VALUE;
- }
-
- // Check input fence is valid.
- if (fence == nullptr) {
- ALOGE("queueBuffer: fence is NULL");
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("queueBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- if (slot < 0 || slot >= max_buffer_count_) {
- ALOGE("queueBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("queueBuffer: slot %d is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if ((!buffers_[slot].mRequestBufferCalled || buffers_[slot].mGraphicBuffer == nullptr)) {
- ALOGE("queueBuffer: slot %d is not requested (mRequestBufferCalled=%d, "
- "mGraphicBuffer=%p)",
- slot, buffers_[slot].mRequestBufferCalled, buffers_[slot].mGraphicBuffer.get());
- return BAD_VALUE;
- }
-
- // Post the producer buffer with timestamp in the metadata.
- const auto& producer_buffer = buffers_[slot].mProducerBuffer;
-
- // Check input crop is not out of boundary of current buffer.
- Rect buffer_rect(producer_buffer->width(), producer_buffer->height());
- Rect cropped_rect(Rect::EMPTY_RECT);
- crop.intersect(buffer_rect, &cropped_rect);
- if (cropped_rect != crop) {
- ALOGE("queueBuffer: slot %d has out-of-boundary crop.", slot);
- return BAD_VALUE;
- }
-
- LocalHandle fence_fd(fence->isValid() ? fence->dup() : -1);
-
- DvrNativeBufferMetadata meta_data;
- meta_data.timestamp = timestamp;
- meta_data.is_auto_timestamp = int32_t(is_auto_timestamp);
- meta_data.dataspace = int32_t(dataspace);
- meta_data.crop_left = crop.left;
- meta_data.crop_top = crop.top;
- meta_data.crop_right = crop.right;
- meta_data.crop_bottom = crop.bottom;
- meta_data.scaling_mode = int32_t(scaling_mode);
- meta_data.transform = int32_t(transform);
-
- producer_buffer->PostAsync(&meta_data, fence_fd);
- buffers_[slot].mBufferState.queue();
-
- output->width = producer_buffer->width();
- output->height = producer_buffer->height();
- output->transformHint = 0; // default value, we don't use it yet.
-
- // |numPendingBuffers| counts of the number of buffers that has been enqueued
- // by the producer but not yet acquired by the consumer. Due to the nature
- // of BufferHubQueue design, this is hard to trace from the producer's client
- // side, but it's safe to assume it's zero.
- output->numPendingBuffers = 0;
-
- // Note that we are not setting nextFrameNumber here as it seems to be only
- // used by surface flinger. See more at b/22802885, ag/791760.
- output->nextFrameNumber = 0;
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ == kNoConnectedApi) {
- ALOGE("cancelBuffer: BufferQueue has no connected producer");
- return NO_INIT;
- }
-
- if (slot < 0 || slot >= max_buffer_count_) {
- ALOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, max_buffer_count_);
- return BAD_VALUE;
- } else if (!buffers_[slot].mBufferState.isDequeued()) {
- ALOGE("cancelBuffer: slot %d is not owned by the producer (state = %s)", slot,
- buffers_[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (fence == nullptr) {
- ALOGE("cancelBuffer: fence is NULL");
- return BAD_VALUE;
- }
-
- auto producer_buffer = buffers_[slot].mProducerBuffer;
- queue_->Enqueue(producer_buffer, size_t(slot), 0U);
- buffers_[slot].mBufferState.cancel();
- buffers_[slot].mFence = fence;
- ALOGV("cancelBuffer: slot %d", slot);
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::query(int what, int* out_value) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (out_value == nullptr) {
- ALOGE("query: out_value was NULL");
- return BAD_VALUE;
- }
-
- int value = 0;
- switch (what) {
- case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
- // TODO(b/36187402) This should be the maximum number of buffers that this
- // producer queue's consumer can acquire. Set to be at least one. Need to
- // find a way to set from the consumer side.
- value = kDefaultUndequeuedBuffers;
- break;
- case NATIVE_WINDOW_BUFFER_AGE:
- value = 0;
- break;
- case NATIVE_WINDOW_WIDTH:
- value = int32_t(queue_->default_width());
- break;
- case NATIVE_WINDOW_HEIGHT:
- value = int32_t(queue_->default_height());
- break;
- case NATIVE_WINDOW_FORMAT:
- value = int32_t(queue_->default_format());
- break;
- case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
- // BufferHubQueue is always operating in async mode, thus semantically
- // consumer can never be running behind. See BufferQueueCore.cpp core
- // for more information about the original meaning of this flag.
- value = 0;
- break;
- case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
- // TODO(jwcai) This is currently not implement as we don't need
- // IGraphicBufferConsumer parity.
- value = 0;
- break;
- case NATIVE_WINDOW_DEFAULT_DATASPACE:
- // TODO(jwcai) Return the default value android::BufferQueue is using as
- // there is no way dvr::ConsumerQueue can set it.
- value = 0; // HAL_DATASPACE_UNKNOWN
- break;
- case NATIVE_WINDOW_STICKY_TRANSFORM:
- // TODO(jwcai) Return the default value android::BufferQueue is using as
- // there is no way dvr::ConsumerQueue can set it.
- value = 0;
- break;
- case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
- // In Daydream's implementation, the consumer end (i.e. VR Compostior)
- // knows how to handle protected buffers.
- value = 1;
- break;
- default:
- return BAD_VALUE;
- }
-
- ALOGV("query: key=%d, v=%d", what, value);
- *out_value = value;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::connect(const sp<IProducerListener>& /* listener */, int api,
- bool /* producer_controlled_by_app */,
- QueueBufferOutput* output) {
- // Consumer interaction are actually handled by buffer hub, and we need
- // to maintain consumer operations here. We only need to perform basic input
- // parameter checks here.
- ALOGV(__FUNCTION__);
-
- if (output == nullptr) {
- return BAD_VALUE;
- }
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (connected_api_ != kNoConnectedApi) {
- return BAD_VALUE;
- }
-
- if (!queue_->is_connected()) {
- ALOGE("BufferHubProducer::connect: This BufferHubProducer is not "
- "connected to bufferhud. Has it been taken out as a parcelable?");
- return BAD_VALUE;
- }
-
- switch (api) {
- case NATIVE_WINDOW_API_EGL:
- case NATIVE_WINDOW_API_CPU:
- case NATIVE_WINDOW_API_MEDIA:
- case NATIVE_WINDOW_API_CAMERA:
- connected_api_ = api;
-
- output->width = queue_->default_width();
- output->height = queue_->default_height();
-
- // default values, we don't use them yet.
- output->transformHint = 0;
- output->numPendingBuffers = 0;
- output->nextFrameNumber = 0;
- output->bufferReplaced = false;
-
- break;
- default:
- ALOGE("BufferHubProducer::connect: unknow API %d", api);
- return BAD_VALUE;
- }
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::disconnect(int api, DisconnectMode /*mode*/) {
- // Consumer interaction are actually handled by buffer hub, and we need
- // to maintain consumer operations here. We only need to perform basic input
- // parameter checks here.
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
-
- if (kNoConnectedApi == connected_api_) {
- return NO_INIT;
- } else if (api != connected_api_) {
- return BAD_VALUE;
- }
-
- FreeAllBuffers();
- connected_api_ = kNoConnectedApi;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setSidebandStream(const sp<NativeHandle>& stream) {
- if (stream != nullptr) {
- // TODO(jwcai) Investigate how is is used, maybe use BufferHubBuffer's
- // metadata.
- ALOGE("SidebandStream is not currently supported.");
- return INVALID_OPERATION;
- }
- return NO_ERROR;
-}
-
-void BufferHubProducer::allocateBuffers(uint32_t /* width */, uint32_t /* height */,
- PixelFormat /* format */, uint64_t /* usage */) {
- // TODO(jwcai) |allocateBuffers| aims to preallocate up to the maximum number
- // of buffers permitted by the current BufferQueue configuration (aka
- // |max_buffer_count_|).
- ALOGE("BufferHubProducer::allocateBuffers not implemented.");
-}
-
-status_t BufferHubProducer::allowAllocation(bool /* allow */) {
- ALOGE("BufferHubProducer::allowAllocation not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubProducer::setGenerationNumber(uint32_t generation_number) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
- generation_number_ = generation_number;
- return NO_ERROR;
-}
-
-String8 BufferHubProducer::getConsumerName() const {
- // BufferHub based implementation could have one to many producer/consumer
- // relationship, thus |getConsumerName| from the producer side does not
- // make any sense.
- ALOGE("BufferHubProducer::getConsumerName not supported.");
- return String8("BufferHubQueue::StubConsumer");
-}
-
-status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) {
- if (shared_buffer_mode) {
- ALOGE("BufferHubProducer::setSharedBufferMode(true) is not supported.");
- // TODO(b/36373181) Front buffer mode for buffer hub queue as ANativeWindow.
- return INVALID_OPERATION;
- }
- // Setting to default should just work as a no-op.
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setAutoRefresh(bool auto_refresh) {
- if (auto_refresh) {
- ALOGE("BufferHubProducer::setAutoRefresh(true) is not supported.");
- return INVALID_OPERATION;
- }
- // Setting to default should just work as a no-op.
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::setDequeueTimeout(nsecs_t timeout) {
- ALOGV(__FUNCTION__);
-
- std::unique_lock<std::mutex> lock(mutex_);
- dequeue_timeout_ms_ = static_cast<int>(timeout / (1000 * 1000));
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::getLastQueuedBuffer(sp<GraphicBuffer>* /* out_buffer */,
- sp<Fence>* /* out_fence */,
- float /*out_transform_matrix*/[16]) {
- ALOGE("BufferHubProducer::getLastQueuedBuffer not implemented.");
- return INVALID_OPERATION;
-}
-
-void BufferHubProducer::getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {
- ALOGE("BufferHubProducer::getFrameTimestamps not implemented.");
-}
-
-status_t BufferHubProducer::getUniqueId(uint64_t* out_id) const {
- ALOGV(__FUNCTION__);
-
- *out_id = unique_id_;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::getConsumerUsage(uint64_t* out_usage) const {
- ALOGV(__FUNCTION__);
-
- // same value as returned by querying NATIVE_WINDOW_CONSUMER_USAGE_BITS
- *out_usage = 0;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::TakeAsParcelable(ProducerQueueParcelable* out_parcelable) {
- if (!out_parcelable || out_parcelable->IsValid()) return BAD_VALUE;
-
- if (connected_api_ != kNoConnectedApi) {
- ALOGE("BufferHubProducer::TakeAsParcelable: BufferHubProducer has "
- "connected client. Must disconnect first.");
- return BAD_VALUE;
- }
-
- if (!queue_->is_connected()) {
- ALOGE("BufferHubProducer::TakeAsParcelable: This BufferHubProducer "
- "is not connected to bufferhud. Has it been taken out as a "
- "parcelable?");
- return BAD_VALUE;
- }
-
- auto status = queue_->TakeAsParcelable();
- if (!status) {
- ALOGE("BufferHubProducer::TakeAsParcelable: Failed to take out "
- "ProducuerQueueParcelable from the producer queue, error: %s.",
- status.GetErrorMessage().c_str());
- return BAD_VALUE;
- }
-
- *out_parcelable = status.take();
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
- PixelFormat format, uint64_t usage) {
- auto status = queue_->AllocateBuffer(width, height, layer_count, uint32_t(format), usage);
- if (!status) {
- ALOGE("BufferHubProducer::AllocateBuffer: Failed to allocate buffer: %s",
- status.GetErrorMessage().c_str());
- return NO_MEMORY;
- }
-
- size_t slot = status.get();
- auto producer_buffer = queue_->GetBuffer(slot);
-
- LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr,
- "Failed to get the producer buffer at slot: %zu", slot);
-
- buffers_[slot].mProducerBuffer = producer_buffer;
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::RemoveBuffer(size_t slot) {
- auto status = queue_->RemoveBuffer(slot);
- if (!status) {
- ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.",
- slot, status.GetErrorMessage().c_str());
- return INVALID_OPERATION;
- }
-
- // Reset in memory objects related the the buffer.
- buffers_[slot].mProducerBuffer = nullptr;
- buffers_[slot].mBufferState.detachProducer();
- buffers_[slot].mFence = Fence::NO_FENCE;
- buffers_[slot].mGraphicBuffer = nullptr;
- buffers_[slot].mRequestBufferCalled = false;
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::FreeAllBuffers() {
- for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
- // Reset in memory objects related the the buffer.
- buffers_[slot].mProducerBuffer = nullptr;
- buffers_[slot].mBufferState.reset();
- buffers_[slot].mFence = Fence::NO_FENCE;
- buffers_[slot].mGraphicBuffer = nullptr;
- buffers_[slot].mRequestBufferCalled = false;
- }
-
- auto status = queue_->FreeAllBuffers();
- if (!status) {
- ALOGE("BufferHubProducer::FreeAllBuffers: Failed to free all buffers on "
- "the queue: %s",
- status.GetErrorMessage().c_str());
- }
-
- if (queue_->capacity() != 0 || queue_->count() != 0) {
- LOG_ALWAYS_FATAL("BufferHubProducer::FreeAllBuffers: Not all buffers are freed.");
- }
-
- return NO_ERROR;
-}
-
-status_t BufferHubProducer::exportToParcel(Parcel* parcel) {
- status_t res = TakeAsParcelable(&pending_producer_parcelable_);
- if (res != NO_ERROR) return res;
-
- if (!pending_producer_parcelable_.IsValid()) {
- ALOGE("BufferHubProducer::exportToParcel: Invalid parcelable object.");
- return BAD_VALUE;
- }
-
- res = parcel->writeUint32(USE_BUFFER_HUB);
- if (res != NO_ERROR) {
- ALOGE("BufferHubProducer::exportToParcel: Cannot write magic, res=%d.", res);
- return res;
- }
-
- return pending_producer_parcelable_.writeToParcel(parcel);
-}
-
-IBinder* BufferHubProducer::onAsBinder() {
- ALOGE("BufferHubProducer::onAsBinder: BufferHubProducer should never be used as an Binder "
- "object.");
- return nullptr;
-}
-
-} // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index c1d92a2..66cad03 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -18,11 +18,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
-#ifndef NO_BUFFERHUB
-#include <gui/BufferHubConsumer.h>
-#include <gui/BufferHubProducer.h>
-#endif
-
#include <gui/BufferQueue.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
@@ -127,32 +122,4 @@
*outConsumer = consumer;
}
-#ifndef NO_BUFFERHUB
-void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer,
- sp<IGraphicBufferConsumer>* outConsumer) {
- LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL");
- LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL");
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
-
- dvr::ProducerQueueConfigBuilder configBuilder;
- std::shared_ptr<dvr::ProducerQueue> producerQueue =
- dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{});
- LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue.");
-
- std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue();
- LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue.");
-
- producer = BufferHubProducer::Create(producerQueue);
- consumer = BufferHubConsumer::Create(consumerQueue);
-
- LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer");
- LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer");
-
- *outProducer = producer;
- *outConsumer = consumer;
-}
-#endif
-
}; // namespace android
diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp
index 52d9540..bd640df 100644
--- a/libs/gui/DisplayInfo.cpp
+++ b/libs/gui/DisplayInfo.cpp
@@ -20,8 +20,13 @@
#include <gui/DisplayInfo.h>
#include <private/gui/ParcelUtils.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
+#include <inttypes.h>
+
+#define INDENT " "
+
namespace android::gui {
// --- DisplayInfo ---
@@ -67,4 +72,17 @@
return OK;
}
+void DisplayInfo::dump(std::string& out, const char* prefix) const {
+ using android::base::StringAppendF;
+
+ out += prefix;
+ StringAppendF(&out, "DisplayViewport[id=%" PRId32 "]\n", displayId);
+ out += prefix;
+ StringAppendF(&out, INDENT "Width=%" PRId32 ", Height=%" PRId32 "\n", logicalWidth,
+ logicalHeight);
+ std::string transformPrefix(prefix);
+ transformPrefix.append(INDENT);
+ transform.dump(out, "Transform", transformPrefix.c_str());
+}
+
} // namespace android::gui
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 797069c..918ff2d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -27,10 +27,6 @@
#include <binder/Parcel.h>
#include <binder/IInterface.h>
-#ifndef NO_BUFFERHUB
-#include <gui/BufferHubProducer.h>
-#endif
-
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
#include <gui/BufferQueueDefs.h>
@@ -1012,17 +1008,7 @@
}
case USE_BUFFER_HUB: {
ALOGE("createFromParcel: BufferHub not implemented.");
-#ifndef NO_BUFFERHUB
- dvr::ProducerQueueParcelable producerParcelable;
- res = producerParcelable.readFromParcel(parcel);
- if (res != NO_ERROR) {
- ALOGE("createFromParcel: Failed to read from parcel, error=%d", res);
- return nullptr;
- }
- return BufferHubProducer::Create(std::move(producerParcelable));
-#else
return nullptr;
-#endif
}
default: {
ALOGE("createFromParcel: Unexpected mgaic: 0x%x.", outMagic);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index af64b3b..a0e75ff 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -19,7 +19,6 @@
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/IRegionSamplingListener.h>
-#include <android/gui/ITransactionTraceListener.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
@@ -43,6 +42,7 @@
namespace android {
+using gui::CallbackId;
using gui::DisplayCaptureArgs;
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
@@ -61,11 +61,11 @@
virtual ~BpSurfaceComposer();
status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken, const InputWindowCommands& commands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ Vector<ComposerState>& state, const Vector<DisplayState>& displays,
+ uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& commands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+ bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) override {
Parcel data, reply;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index e4b8bad..23d7d50 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -17,23 +17,15 @@
#define LOG_TAG "ITransactionCompletedListener"
//#define LOG_NDEBUG 0
+#include <cstdint>
+#include <optional>
+
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
#include <private/gui/ParcelUtils.h>
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
- ON_RELEASE_BUFFER,
- ON_TRANSACTION_QUEUE_STALLED,
- LAST = ON_RELEASE_BUFFER,
-};
-
-} // Anonymous namespace
+namespace android::gui {
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t err = output->writeUint64(frameNumber);
@@ -126,7 +118,12 @@
} else {
SAFE_PARCEL(output->writeBool, false);
}
- SAFE_PARCEL(output->writeUint32, transformHint);
+
+ SAFE_PARCEL(output->writeBool, transformHint.has_value());
+ if (transformHint.has_value()) {
+ output->writeUint32(transformHint.value());
+ }
+
SAFE_PARCEL(output->writeUint32, currentMaxAcquiredBufferCount);
SAFE_PARCEL(output->writeParcelable, eventStats);
SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankData.size()));
@@ -156,7 +153,16 @@
previousReleaseFence = new Fence();
SAFE_PARCEL(input->read, *previousReleaseFence);
}
- SAFE_PARCEL(input->readUint32, &transformHint);
+ bool hasTransformHint = false;
+ SAFE_PARCEL(input->readBool, &hasTransformHint);
+ if (hasTransformHint) {
+ uint32_t tempTransformHint;
+ SAFE_PARCEL(input->readUint32, &tempTransformHint);
+ transformHint = std::make_optional(tempTransformHint);
+ } else {
+ transformHint = std::nullopt;
+ }
+
SAFE_PARCEL(input->readUint32, ¤tMaxAcquiredBufferCount);
SAFE_PARCEL(input->readParcelable, &eventStats);
@@ -257,58 +263,6 @@
return listenerStats;
}
-class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
-public:
- explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
- : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
- }
-
- ~BpTransactionCompletedListener() override;
-
- void onTransactionCompleted(ListenerStats stats) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
- stats);
- }
-
- void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override {
- callRemoteAsync<decltype(
- &ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
- callbackId, releaseFence,
- currentMaxAcquiredBufferCount);
- }
-
- void onTransactionQueueStalled() override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::onTransactionQueueStalled)>(
- Tag::ON_TRANSACTION_QUEUE_STALLED);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
-
-IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
-
-status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_TRANSACTION_COMPLETED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionCompleted);
- case Tag::ON_RELEASE_BUFFER:
- return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
- case Tag::ON_TRANSACTION_QUEUE_STALLED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionQueueStalled);
- }
-}
-
ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
std::vector<CallbackId> filteredCallbackIds;
for (const auto& callbackId : callbackIds) {
@@ -347,4 +301,4 @@
const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
-}; // namespace android
+}; // namespace android::gui
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4d5978c..0d1a69b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -24,12 +24,34 @@
#include <binder/Parcel.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
+#include <gui/SurfaceControl.h>
#include <private/gui/ParcelUtils.h>
#include <system/window.h>
#include <utils/Errors.h>
+#define CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD) \
+ { \
+ if ((OTHER.what & CHANGE_FLAG) && (FIELD != OTHER.FIELD)) { \
+ DIFF_RESULT |= CHANGE_FLAG; \
+ } \
+ }
+
+#define CHECK_DIFF2(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2) \
+ { \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \
+ }
+
+#define CHECK_DIFF3(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2, FIELD3) \
+ { \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD3) \
+ }
+
namespace android {
+using gui::CallbackId;
using gui::FocusRequest;
using gui::WindowInfoHandle;
@@ -40,13 +62,13 @@
x(0),
y(0),
z(0),
- alpha(0),
flags(0),
mask(0),
reserved(0),
cornerRadius(0.0f),
backgroundBlurRadius(0),
- transform(0),
+ color(0),
+ bufferTransform(0),
transformToDisplayInverse(false),
crop(Rect::INVALID_RECT),
dataspace(ui::Dataspace::UNKNOWN),
@@ -83,20 +105,19 @@
SAFE_PARCEL(output.writeFloat, y);
SAFE_PARCEL(output.writeInt32, z);
SAFE_PARCEL(output.writeUint32, layerStack.id);
- SAFE_PARCEL(output.writeFloat, alpha);
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
SAFE_PARCEL(matrix.write, output);
SAFE_PARCEL(output.write, crop);
- SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
SAFE_PARCEL(output.writeFloat, color.r);
SAFE_PARCEL(output.writeFloat, color.g);
SAFE_PARCEL(output.writeFloat, color.b);
+ SAFE_PARCEL(output.writeFloat, color.a);
SAFE_PARCEL(windowInfoHandle->writeToParcel, &output);
SAFE_PARCEL(output.write, transparentRegion);
- SAFE_PARCEL(output.writeUint32, transform);
+ SAFE_PARCEL(output.writeUint32, bufferTransform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
SAFE_PARCEL(output.writeBool, borderEnabled);
SAFE_PARCEL(output.writeFloat, borderWidth);
@@ -177,7 +198,6 @@
SAFE_PARCEL(input.readFloat, &y);
SAFE_PARCEL(input.readInt32, &z);
SAFE_PARCEL(input.readUint32, &layerStack.id);
- SAFE_PARCEL(input.readFloat, &alpha);
SAFE_PARCEL(input.readUint32, &flags);
@@ -185,7 +205,6 @@
SAFE_PARCEL(matrix.read, input);
SAFE_PARCEL(input.read, crop);
- SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
@@ -197,10 +216,13 @@
color.g = tmpFloat;
SAFE_PARCEL(input.readFloat, &tmpFloat);
color.b = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.a = tmpFloat;
+
SAFE_PARCEL(windowInfoHandle->readFromParcel, &input);
SAFE_PARCEL(input.read, transparentRegion);
- SAFE_PARCEL(input.readUint32, &transform);
+ SAFE_PARCEL(input.readUint32, &bufferTransform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
SAFE_PARCEL(input.readBool, &borderEnabled);
SAFE_PARCEL(input.readFloat, &tmpFloat);
@@ -367,6 +389,27 @@
}
}
+void DisplayState::sanitize(int32_t permissions) {
+ if (what & DisplayState::eLayerStackChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eLayerStackChanged;
+ ALOGE("Stripped attempt to set eLayerStackChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eDisplayProjectionChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eDisplayProjectionChanged;
+ ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eSurfaceChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eSurfaceChanged;
+ ALOGE("Stripped attempt to set eSurfaceChanged in sanitize");
+ }
+ }
+}
+
void layer_state_t::sanitize(int32_t permissions) {
// TODO: b/109894387
//
@@ -453,7 +496,7 @@
}
if (other.what & eAlphaChanged) {
what |= eAlphaChanged;
- alpha = other.alpha;
+ color.a = other.color.a;
}
if (other.what & eMatrixChanged) {
what |= eMatrixChanged;
@@ -495,12 +538,9 @@
what |= eReparent;
parentSurfaceControlForChild = other.parentSurfaceControlForChild;
}
- if (other.what & eDestroySurface) {
- what |= eDestroySurface;
- }
- if (other.what & eTransformChanged) {
- what |= eTransformChanged;
- transform = other.transform;
+ if (other.what & eBufferTransformChanged) {
+ what |= eBufferTransformChanged;
+ bufferTransform = other.bufferTransform;
}
if (other.what & eTransformToDisplayInverseChanged) {
what |= eTransformToDisplayInverseChanged;
@@ -547,7 +587,7 @@
}
if (other.what & eBackgroundColorChanged) {
what |= eBackgroundColorChanged;
- color = other.color;
+ color.rgb = other.color.rgb;
bgColorAlpha = other.bgColorAlpha;
bgColorDataspace = other.bgColorDataspace;
}
@@ -612,7 +652,7 @@
}
if (other.what & eColorChanged) {
what |= eColorChanged;
- color = other.color;
+ color.rgb = other.color.rgb;
}
if (other.what & eColorSpaceAgnosticChanged) {
what |= eColorSpaceAgnosticChanged;
@@ -629,12 +669,80 @@
}
}
+uint64_t layer_state_t::diff(const layer_state_t& other) const {
+ uint64_t diff = 0;
+ CHECK_DIFF2(diff, ePositionChanged, other, x, y);
+ if (other.what & eLayerChanged) {
+ diff |= eLayerChanged;
+ diff &= ~eRelativeLayerChanged;
+ }
+ CHECK_DIFF(diff, eAlphaChanged, other, color.a);
+ CHECK_DIFF(diff, eMatrixChanged, other, matrix);
+ if (other.what & eTransparentRegionChanged &&
+ (!transparentRegion.hasSameRects(other.transparentRegion))) {
+ diff |= eTransparentRegionChanged;
+ }
+ if (other.what & eFlagsChanged) {
+ uint64_t changedFlags = (flags & other.mask) ^ (other.flags & other.mask);
+ if (changedFlags) diff |= eFlagsChanged;
+ }
+ CHECK_DIFF(diff, eLayerStackChanged, other, layerStack);
+ CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius);
+ CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius);
+ if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged;
+ if (other.what & eRelativeLayerChanged) {
+ diff |= eRelativeLayerChanged;
+ diff &= ~eLayerChanged;
+ }
+ if (other.what & eReparent &&
+ !SurfaceControl::isSameSurface(parentSurfaceControlForChild,
+ other.parentSurfaceControlForChild)) {
+ diff |= eReparent;
+ }
+ CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform);
+ CHECK_DIFF(diff, eTransformToDisplayInverseChanged, other, transformToDisplayInverse);
+ CHECK_DIFF(diff, eCropChanged, other, crop);
+ if (other.what & eBufferChanged) diff |= eBufferChanged;
+ CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
+ CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
+ if (other.what & eSurfaceDamageRegionChanged &&
+ (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
+ diff |= eSurfaceDamageRegionChanged;
+ }
+ CHECK_DIFF(diff, eApiChanged, other, api);
+ if (other.what & eSidebandStreamChanged) diff |= eSidebandStreamChanged;
+ CHECK_DIFF(diff, eApiChanged, other, api);
+ CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform);
+ if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged;
+ if (other.what & eInputInfoChanged) diff |= eInputInfoChanged;
+ CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace);
+ if (other.what & eMetadataChanged) diff |= eMetadataChanged;
+ CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
+ CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor);
+ CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
+ CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
+ CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
+ changeFrameRateStrategy);
+ CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
+ CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
+ CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
+ CHECK_DIFF(diff, eStretchChanged, other, stretchEffect);
+ CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop);
+ CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame);
+ if (other.what & eProducerDisconnect) diff |= eProducerDisconnect;
+ CHECK_DIFF(diff, eDropInputModeChanged, other, dropInputMode);
+ CHECK_DIFF(diff, eColorChanged, other, color.rgb);
+ CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
+ CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
+ return diff;
+}
+
bool layer_state_t::hasBufferChanges() const {
return what & layer_state_t::eBufferChanged;
}
bool layer_state_t::hasValidBuffer() const {
- return bufferData && (bufferData->buffer || bufferData->cachedBuffer.isValid());
+ return bufferData && (bufferData->hasBuffer() || bufferData->cachedBuffer.isValid());
}
status_t layer_state_t::matrix22_t::write(Parcel& output) const {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 4b4d46a..edb18a8 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -56,6 +56,12 @@
namespace {
+enum {
+ // moved from nativewindow/include/system/window.h, to be removed
+ NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
+ NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+};
+
bool isInterceptorRegistrationOp(int op) {
return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
@@ -348,34 +354,25 @@
return NO_ERROR;
}
+// Deprecated(b/242763577): to be removed, this method should not be used
+// The reason this method still exists here is to support compiled vndk
+// Surface support should not be tied to the display
+// Return true since most displays should have this support
status_t Surface::getWideColorSupport(bool* supported) {
ATRACE_CALL();
- const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
- if (display == nullptr) {
- return NAME_NOT_FOUND;
- }
-
- *supported = false;
- binder::Status status = composerServiceAIDL()->isWideColorDisplay(display, supported);
- return statusTFromBinderStatus(status);
+ *supported = true;
+ return NO_ERROR;
}
+// Deprecated(b/242763577): to be removed, this method should not be used
+// The reason this method still exists here is to support compiled vndk
+// Surface support should not be tied to the display
+// Return true since most displays should have this support
status_t Surface::getHdrSupport(bool* supported) {
ATRACE_CALL();
- const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
- if (display == nullptr) {
- return NAME_NOT_FOUND;
- }
-
- gui::DynamicDisplayInfo info;
- if (binder::Status status = composerServiceAIDL()->getDynamicDisplayInfo(display, &info);
- !status.isOk()) {
- return statusTFromBinderStatus(status);
- }
-
- *supported = !info.hdrCapabilities.supportedHdrTypes.empty();
+ *supported = true;
return NO_ERROR;
}
@@ -1111,9 +1108,12 @@
ATRACE_CALL();
auto& mapper = GraphicBufferMapper::get();
mapper.setDataspace(buffer->handle, static_cast<ui::Dataspace>(queueBufferInput.dataSpace));
- mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086());
- mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613());
- mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus());
+ if (mHdrMetadataIsSet & HdrMetadata::SMPTE2086)
+ mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086());
+ if (mHdrMetadataIsSet & HdrMetadata::CTA861_3)
+ mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613());
+ if (mHdrMetadataIsSet & HdrMetadata::HDR10PLUS)
+ mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus());
}
void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,
@@ -2255,6 +2255,7 @@
int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) {
ALOGV("Surface::setBuffersSmpte2086Metadata");
Mutex::Autolock lock(mMutex);
+ mHdrMetadataIsSet |= HdrMetadata::SMPTE2086;
if (metadata) {
mHdrMetadata.smpte2086 = *metadata;
mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
@@ -2267,6 +2268,7 @@
int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) {
ALOGV("Surface::setBuffersCta8613Metadata");
Mutex::Autolock lock(mMutex);
+ mHdrMetadataIsSet |= HdrMetadata::CTA861_3;
if (metadata) {
mHdrMetadata.cta8613 = *metadata;
mHdrMetadata.validTypes |= HdrMetadata::CTA861_3;
@@ -2279,6 +2281,7 @@
int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
ALOGV("Surface::setBuffersBlobMetadata");
Mutex::Autolock lock(mMutex);
+ mHdrMetadataIsSet |= HdrMetadata::HDR10PLUS;
if (size > 0) {
mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 72bd1fb..d741c99 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "SurfaceComposerClient"
+#include <semaphore.h>
#include <stdint.h>
#include <sys/types.h>
@@ -24,6 +25,7 @@
#include <android/gui/ISurfaceComposerClient.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/os/IInputConstants.h>
+#include <gui/TraceUtils.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/SortedVector.h>
@@ -312,7 +314,8 @@
}
}
-void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+binder::Status TransactionCompletedListener::onTransactionCompleted(
+ const ListenerStats& listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
@@ -356,7 +359,8 @@
transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats);
+ surfaceStats.eventStats,
+ surfaceStats.currentMaxAcquiredBufferCount);
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
@@ -381,11 +385,13 @@
transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
transactionStats.presentFence,
surfaceStats.previousReleaseFence, surfaceStats.transformHint,
- surfaceStats.eventStats);
- if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
+ surfaceStats.eventStats,
+ surfaceStats.currentMaxAcquiredBufferCount);
+ if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl] &&
+ surfaceStats.transformHint.has_value()) {
callbacksMap[callbackId]
.surfaceControls[surfaceStats.surfaceControl]
- ->setTransformHint(surfaceStats.transformHint);
+ ->setTransformHint(*surfaceStats.transformHint);
}
// If there is buffer id set, we look up any pending client release buffer callbacks
// and call them. This is a performance optimization when we have a transaction
@@ -449,32 +455,38 @@
}
}
}
+ return binder::Status::ok();
}
-void TransactionCompletedListener::onTransactionQueueStalled() {
- std::unordered_map<void*, std::function<void()>> callbackCopy;
- {
- std::scoped_lock<std::mutex> lock(mMutex);
- callbackCopy = mQueueStallListeners;
- }
- for (auto const& it : callbackCopy) {
- it.second();
- }
+binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) {
+ std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ callbackCopy = mQueueStallListeners;
+ }
+ for (auto const& it : callbackCopy) {
+ it.second(reason.c_str());
+ }
+ return binder::Status::ok();
}
-void TransactionCompletedListener::addQueueStallListener(std::function<void()> stallListener,
- void* id) {
+void TransactionCompletedListener::addQueueStallListener(
+ std::function<void(const std::string&)> stallListener, void* id) {
std::scoped_lock<std::mutex> lock(mMutex);
mQueueStallListeners[id] = stallListener;
}
-void TransactionCompletedListener::removeQueueStallListener(void *id) {
+
+void TransactionCompletedListener::removeQueueStallListener(void* id) {
std::scoped_lock<std::mutex> lock(mMutex);
mQueueStallListeners.erase(id);
}
-void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
- sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) {
+binder::Status TransactionCompletedListener::onReleaseBuffer(
+ const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) {
+ sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get()))
+ : Fence::NO_FENCE);
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -483,13 +495,14 @@
if (!callback) {
ALOGE("Could not call release buffer callback, buffer not found %s",
callbackId.to_string().c_str());
- return;
+ return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
}
std::optional<uint32_t> optionalMaxAcquiredBufferCount =
- currentMaxAcquiredBufferCount == UINT_MAX
+ static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX
? std::nullopt
: std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
+ return binder::Status::ok();
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -819,7 +832,11 @@
->mReleaseCallbackThread
.addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
} else {
- listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (fence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get())));
+ }
+ listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX);
}
}
@@ -906,10 +923,15 @@
client_cache_t uncacheBuffer;
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
-
- sf->setTransactionState(FrameTimelineInfo{}, {}, {}, ISurfaceComposer::eOneWay,
- Transaction::getDefaultApplyToken(), {}, systemTime(), true,
- uncacheBuffer, false, {}, generateId());
+ Vector<ComposerState> composerStates;
+ status_t status =
+ sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+ ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(),
+ {}, systemTime(), true, uncacheBuffer, false, {}, generateId());
+ if (status != NO_ERROR) {
+ ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
+ strerror(-status));
+ }
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -958,12 +980,55 @@
}
}
+class SyncCallback {
+public:
+ static void function(void* callbackContext, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context for SyncCallback");
+ }
+ SyncCallback* helper = static_cast<SyncCallback*>(callbackContext);
+ LOG_ALWAYS_FATAL_IF(sem_post(&helper->mSemaphore), "sem_post failed");
+ }
+ ~SyncCallback() {
+ if (mInitialized) {
+ LOG_ALWAYS_FATAL_IF(sem_destroy(&mSemaphore), "sem_destroy failed");
+ }
+ }
+ void init() {
+ LOG_ALWAYS_FATAL_IF(clock_gettime(CLOCK_MONOTONIC, &mTimeoutTimespec) == -1,
+ "clock_gettime() fail! in SyncCallback::init");
+ mTimeoutTimespec.tv_sec += 4;
+ LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed");
+ mInitialized = true;
+ }
+ void wait() {
+ int result = sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &mTimeoutTimespec);
+ if (result && errno != ETIMEDOUT && errno != EINTR) {
+ LOG_ALWAYS_FATAL("sem_clockwait failed(%d)", errno);
+ } else if (errno == ETIMEDOUT) {
+ ALOGW("Sync transaction timed out waiting for commit callback.");
+ }
+ }
+ void* getContext() { return static_cast<void*>(this); }
+
+private:
+ sem_t mSemaphore;
+ bool mInitialized = false;
+ timespec mTimeoutTimespec;
+};
+
status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
if (mStatus != NO_ERROR) {
return mStatus;
}
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ SyncCallback syncCallback;
+ if (synchronous) {
+ syncCallback.init();
+ addTransactionCommittedCallback(syncCallback.function, syncCallback.getContext());
+ }
bool hasListenerCallbacks = !mListenerCallbacks.empty();
std::vector<ListenerCallbacks> listenerCallbacks;
@@ -998,15 +1063,12 @@
Vector<DisplayState> displayStates;
uint32_t flags = 0;
- for (auto const& kv : mComposerStates){
+ for (auto const& kv : mComposerStates) {
composerStates.add(kv.second);
}
displayStates = std::move(mDisplayStates);
- if (synchronous) {
- flags |= ISurfaceComposer::eSynchronous;
- }
if (mAnimation) {
flags |= ISurfaceComposer::eAnimation;
}
@@ -1030,6 +1092,7 @@
sp<IBinder> applyToken = mApplyToken ? mApplyToken : sApplyToken;
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
@@ -1039,6 +1102,10 @@
// Clear the current states and flags
clear();
+ if (synchronous) {
+ syncCallback.wait();
+ }
+
mStatus = NO_ERROR;
return NO_ERROR;
}
@@ -1082,11 +1149,6 @@
return physicalDisplayIds;
}
-std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() {
- ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
- return instance.getInternalDisplayId();
-}
-
sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) {
sp<IBinder> display = nullptr;
binder::Status status =
@@ -1095,11 +1157,6 @@
return status.isOk() ? display : nullptr;
}
-sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() {
- ComposerServiceAIDL& instance = ComposerServiceAIDL::getInstance();
- return instance.getInternalDisplayToken();
-}
-
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
mAnimation = true;
}
@@ -1204,6 +1261,7 @@
if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
(mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
(mask & layer_state_t::eEnableBackpressure) ||
+ (mask & layer_state_t::eIgnoreDestinationFrame) ||
(mask & layer_state_t::eLayerIsDisplayDecoration)) {
s->what |= layer_state_t::eFlagsChanged;
}
@@ -1255,7 +1313,7 @@
ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha);
}
s->what |= layer_state_t::eAlphaChanged;
- s->alpha = std::clamp(alpha, 0.f, 1.f);
+ s->color.a = std::clamp(alpha, 0.f, 1.f);
registerSurfaceControlForCallback(sc);
return *this;
@@ -1386,7 +1444,7 @@
return *this;
}
s->what |= layer_state_t::eColorChanged;
- s->color = color;
+ s->color.rgb = color;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1401,7 +1459,7 @@
}
s->what |= layer_state_t::eBackgroundColorChanged;
- s->color = color;
+ s->color.rgb = color;
s->bgColorAlpha = alpha;
s->bgColorDataspace = dataspace;
@@ -1416,8 +1474,8 @@
mStatus = BAD_INDEX;
return *this;
}
- s->what |= layer_state_t::eTransformChanged;
- s->transform = transform;
+ s->what |= layer_state_t::eBufferTransformChanged;
+ s->bufferTransform = transform;
registerSurfaceControlForCallback(sc);
return *this;
@@ -2108,6 +2166,10 @@
return s;
}
+static std::string toString(const String16& string) {
+ return std::string(String8(string).c_str());
+}
+
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface, int32_t flags,
@@ -2127,7 +2189,8 @@
}
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, result.handle, result.layerId, w, h, format,
+ *outSurface = new SurfaceControl(this, result.handle, result.layerId,
+ toString(result.layerName), w, h, format,
result.transformHint, flags);
}
}
@@ -2140,21 +2203,21 @@
}
sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
- gui::MirrorSurfaceResult result;
+ gui::CreateSurfaceResult result;
const binder::Status status = mClient->mirrorSurface(mirrorFromHandle, &result);
const status_t err = statusTFromBinderStatus(status);
if (err == NO_ERROR) {
- return new SurfaceControl(this, result.handle, result.layerId);
+ return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
}
return nullptr;
}
sp<SurfaceControl> SurfaceComposerClient::mirrorDisplay(DisplayId displayId) {
- gui::MirrorSurfaceResult result;
+ gui::CreateSurfaceResult result;
const binder::Status status = mClient->mirrorDisplay(displayId.value, &result);
const status_t err = statusTFromBinderStatus(status);
if (err == NO_ERROR) {
- return new SurfaceControl(this, result.handle, result.layerId);
+ return new SurfaceControl(this, result.handle, result.layerId, toString(result.layerName));
}
return nullptr;
}
@@ -2194,18 +2257,6 @@
// ----------------------------------------------------------------------------
-status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- binder::Status status = sf->enableVSyncInjections(enable);
- return statusTFromBinderStatus(status);
-}
-
-status_t SurfaceComposerClient::injectVSync(nsecs_t when) {
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- binder::Status status = sf->injectVSync(when);
- return statusTFromBinderStatus(status);
-}
-
status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display,
ui::DisplayState* state) {
gui::DisplayState ds;
@@ -2220,12 +2271,12 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display,
+status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId,
ui::StaticDisplayInfo* outInfo) {
using Tag = android::gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
gui::StaticDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(displayId, &ginfo);
if (status.isOk()) {
// convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
@@ -2269,52 +2320,74 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display,
- ui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo) {
+ // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
+ for (const auto& mode : ginfo.supportedDisplayModes) {
+ ui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const int32_t& value) { return static_cast<ui::Hdr>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
+ outInfo->renderFrameRate = ginfo.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
+ for (const auto& cmode : ginfo.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
+
+ std::vector<ui::Hdr> types;
+ types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
+ for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
+ types.push_back(static_cast<ui::Hdr>(hdr));
+ }
+ outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
+ ginfo.hdrCapabilities.maxAverageLuminance,
+ ginfo.hdrCapabilities.minLuminance);
+
+ outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+}
+
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
+ ui::DynamicDisplayInfo* outInfo) {
gui::DynamicDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromId(displayId,
+ &ginfo);
if (status.isOk()) {
- // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
- for (const auto& mode : ginfo.supportedDisplayModes) {
- ui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
+ }
+ return statusTFromBinderStatus(status);
+}
- outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
- for (const auto& cmode : ginfo.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
-
- std::vector<ui::Hdr> types;
- types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
- for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
- types.push_back(static_cast<ui::Hdr>(hdr));
- }
- outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
- ginfo.hdrCapabilities.maxAverageLuminance,
- ginfo.hdrCapabilities.minLuminance);
-
- outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo* outInfo) {
+ gui::DynamicDisplayInfo ginfo;
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromToken(display,
+ &ginfo);
+ if (status.isOk()) {
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
}
return statusTFromBinderStatus(status);
}
@@ -2322,7 +2395,8 @@
status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
ui::DisplayMode* mode) {
ui::DynamicDisplayInfo info;
- status_t result = getDynamicDisplayInfo(display, &info);
+
+ status_t result = getDynamicDisplayInfoFromToken(display, &info);
if (result != NO_ERROR) {
return result;
}
@@ -2336,42 +2410,22 @@
return NAME_NOT_FOUND;
}
-status_t SurfaceComposerClient::setDesiredDisplayModeSpecs(
- const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
- float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+status_t SurfaceComposerClient::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ const gui::DisplayModeSpecs& specs) {
binder::Status status =
- ComposerServiceAIDL::getComposerService()
- ->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
- primaryRefreshRateMin, primaryRefreshRateMax,
- appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ ComposerServiceAIDL::getComposerService()->setDesiredDisplayModeSpecs(displayToken,
+ specs);
return statusTFromBinderStatus(status);
}
status_t SurfaceComposerClient::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
- ui::DisplayModeId* outDefaultMode,
- bool* outAllowGroupSwitching,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
- if (!outDefaultMode || !outAllowGroupSwitching || !outPrimaryRefreshRateMin ||
- !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+ gui::DisplayModeSpecs* outSpecs) {
+ if (!outSpecs) {
return BAD_VALUE;
}
- gui::DisplayModeSpecs specs;
binder::Status status =
ComposerServiceAIDL::getComposerService()->getDesiredDisplayModeSpecs(displayToken,
- &specs);
- if (status.isOk()) {
- *outDefaultMode = specs.defaultMode;
- *outAllowGroupSwitching = specs.allowGroupSwitching;
- *outPrimaryRefreshRateMin = specs.primaryRefreshRateMin;
- *outPrimaryRefreshRateMax = specs.primaryRefreshRateMax;
- *outAppRequestRefreshRateMin = specs.appRequestRefreshRateMin;
- *outAppRequestRefreshRateMax = specs.appRequestRefreshRateMax;
- }
+ outSpecs);
return statusTFromBinderStatus(status);
}
@@ -2414,6 +2468,12 @@
return statusTFromBinderStatus(status);
}
+status_t SurfaceComposerClient::getOverlaySupport(gui::OverlayProperties* outProperties) {
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getOverlaySupport(outProperties);
+ return statusTFromBinderStatus(status);
+}
+
status_t SurfaceComposerClient::setBootDisplayMode(const sp<IBinder>& display,
ui::DisplayModeId displayModeId) {
binder::Status status = ComposerServiceAIDL::getComposerService()
@@ -2512,7 +2572,7 @@
gui::PullAtomData pad;
binder::Status status = ComposerServiceAIDL::getComposerService()->onPullAtom(atomId, &pad);
if (status.isOk()) {
- outData->assign((const char*)pad.data.data(), pad.data.size());
+ outData->assign(pad.data.begin(), pad.data.end());
*success = pad.success;
}
return statusTFromBinderStatus(status);
@@ -2702,8 +2762,7 @@
ComposerServiceAIDL::getComposerService()->getDisplayDecorationSupport(displayToken,
&gsupport);
std::optional<DisplayDecorationSupport> support;
- // TODO (b/241277093): Remove `false && ` once b/241278870 is fixed.
- if (false && status.isOk() && gsupport.has_value()) {
+ if (status.isOk() && gsupport.has_value()) {
support.emplace(DisplayDecorationSupport{
.format =
static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
@@ -2798,7 +2857,11 @@
while (!callbackInfos.empty()) {
auto [callbackId, releaseFence] = callbackInfos.front();
- listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX);
callbackInfos.pop();
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 84257de..7aee882 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -49,11 +49,12 @@
// ============================================================================
SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- int32_t layerId, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t transform, uint32_t flags)
+ int32_t layerId, const std::string& name, uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t transform, uint32_t flags)
: mClient(client),
mHandle(handle),
mLayerId(layerId),
+ mName(name),
mTransformHint(transform),
mWidth(w),
mHeight(h),
@@ -65,6 +66,7 @@
mHandle = other->mHandle;
mTransformHint = other->mTransformHint;
mLayerId = other->mLayerId;
+ mName = other->mName;
mWidth = other->mWidth;
mHeight = other->mHeight;
mFormat = other->mFormat;
@@ -185,6 +187,10 @@
return mLayerId;
}
+const std::string& SurfaceControl::getName() const {
+ return mName;
+}
+
sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer()
{
getSurface();
@@ -212,6 +218,7 @@
SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient()));
SAFE_PARCEL(parcel.writeStrongBinder, mHandle);
SAFE_PARCEL(parcel.writeInt32, mLayerId);
+ SAFE_PARCEL(parcel.writeUtf8AsUtf16, mName);
SAFE_PARCEL(parcel.writeUint32, mTransformHint);
SAFE_PARCEL(parcel.writeUint32, mWidth);
SAFE_PARCEL(parcel.writeUint32, mHeight);
@@ -225,6 +232,7 @@
sp<IBinder> client;
sp<IBinder> handle;
int32_t layerId;
+ std::string layerName;
uint32_t transformHint;
uint32_t width;
uint32_t height;
@@ -233,16 +241,17 @@
SAFE_PARCEL(parcel.readStrongBinder, &client);
SAFE_PARCEL(parcel.readStrongBinder, &handle);
SAFE_PARCEL(parcel.readInt32, &layerId);
+ SAFE_PARCEL(parcel.readUtf8FromUtf16, &layerName);
SAFE_PARCEL(parcel.readUint32, &transformHint);
SAFE_PARCEL(parcel.readUint32, &width);
SAFE_PARCEL(parcel.readUint32, &height);
SAFE_PARCEL(parcel.readUint32, &format);
// We aren't the original owner of the surface.
- *outSurfaceControl =
- new SurfaceControl(new SurfaceComposerClient(
- interface_cast<ISurfaceComposerClient>(client)),
- handle.get(), layerId, width, height, format, transformHint);
+ *outSurfaceControl = new SurfaceControl(new SurfaceComposerClient(
+ interface_cast<ISurfaceComposerClient>(client)),
+ handle.get(), layerId, layerName, width, height, format,
+ transformHint);
return NO_ERROR;
}
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index 1a8fc1a..2d863c2 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -36,8 +36,12 @@
mHasFenceSync(false),
mHasWaitSync(false) {
EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- // This can only be called after EGL has been initialized; otherwise the
- // check below will abort.
+ // eglQueryString can only be called after EGL has been initialized;
+ // otherwise the check below will abort. If RenderEngine is using SkiaVk,
+ // EGL will not have been initialized. There's no problem with initializing
+ // it again here (it is ref counted), and then terminating it later.
+ EGLBoolean initialized = eglInitialize(dpy, nullptr, nullptr);
+ LOG_ALWAYS_FATAL_IF(!initialized, "eglInitialize failed");
const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed");
if (strstr(exts, "EGL_ANDROID_native_fence_sync")) {
@@ -63,6 +67,8 @@
mString.append(" EGL_KHR_wait_sync");
}
mString.append("]");
+ // Terminate EGL to match the eglInitialize above
+ eglTerminate(dpy);
}
bool SyncFeatures::useNativeFenceSync() const {
diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp
deleted file mode 100644
index 59450fb..0000000
--- a/libs/gui/TransactionTracing.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2020 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 "gui/TransactionTracing.h"
-#include "android/gui/ISurfaceComposer.h"
-
-#include <private/gui/ComposerServiceAIDL.h>
-
-namespace android {
-
-sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr;
-std::mutex TransactionTraceListener::sMutex;
-
-TransactionTraceListener::TransactionTraceListener() {}
-
-sp<TransactionTraceListener> TransactionTraceListener::getInstance() {
- const std::lock_guard<std::mutex> lock(sMutex);
-
- if (sInstance == nullptr) {
- sInstance = new TransactionTraceListener;
-
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- sf->addTransactionTraceListener(sInstance);
- }
-
- return sInstance;
-}
-
-binder::Status TransactionTraceListener::onToggled(bool enabled) {
- ALOGD("TransactionTraceListener: onToggled listener called");
- mTracingEnabled = enabled;
-
- return binder::Status::ok();
-}
-
-bool TransactionTraceListener::isTracingEnabled() {
- return mTracingEnabled;
-}
-
-} // namespace android
diff --git a/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl b/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl
index 39e4916..eea12dc 100644
--- a/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl
+++ b/libs/gui/aidl/android/gui/CreateSurfaceResult.aidl
@@ -20,5 +20,6 @@
parcelable CreateSurfaceResult {
IBinder handle;
int layerId;
+ String layerName;
int transformHint;
}
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index 3cd77f8..ce30426 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -27,6 +27,7 @@
Size resolution;
float xDpi = 0.0f;
float yDpi = 0.0f;
+ int[] supportedHdrTypes;
float refreshRate = 0.0f;
long appVsyncOffset = 0;
diff --git a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl
index fb4fcdf..af138c7 100644
--- a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl
+++ b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl
@@ -18,10 +18,58 @@
/** @hide */
parcelable DisplayModeSpecs {
+ /**
+ * Defines the refresh rates ranges that should be used by SF.
+ */
+ parcelable RefreshRateRanges {
+ /**
+ * Defines a range of refresh rates.
+ */
+ parcelable RefreshRateRange {
+ float min;
+ float max;
+ }
+
+ /**
+ * The range of refresh rates that the display should run at.
+ */
+ RefreshRateRange physical;
+
+ /**
+ * The range of refresh rates that apps should render at.
+ */
+ RefreshRateRange render;
+ }
+
+ /**
+ * Base mode ID. This is what system defaults to for all other settings, or
+ * if the refresh rate range is not available.
+ */
int defaultMode;
+
+ /**
+ * If true this will allow switching between modes in different display configuration
+ * groups. This way the user may see visual interruptions when the display mode changes.
+ */
+
boolean allowGroupSwitching;
- float primaryRefreshRateMin;
- float primaryRefreshRateMax;
- float appRequestRefreshRateMin;
- float appRequestRefreshRateMax;
+
+ /**
+ * The primary physical and render refresh rate ranges represent DisplayManager's general
+ * guidance on the display modes SurfaceFlinger will consider when switching refresh
+ * rates and scheduling the frame rate. Unless SurfaceFlinger has a specific reason to do
+ * otherwise, it will stay within this range.
+ */
+ RefreshRateRanges primaryRanges;
+
+ /**
+ * The app request physical and render refresh rate ranges allow SurfaceFlinger to consider
+ * more display modes when switching refresh rates. Although SurfaceFlinger will
+ * generally stay within the primary range, specific considerations, such as layer frame
+ * rate settings specified via the setFrameRate() API, may cause SurfaceFlinger to go
+ * outside the primary range. SurfaceFlinger never goes outside the app request range.
+ * The app request range will be greater than or equal to the primary refresh rate range,
+ * never smaller.
+ */
+ RefreshRateRanges appRequestRanges;
}
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 57e6081..3114929 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -27,6 +27,7 @@
List<DisplayMode> supportedDisplayModes;
int activeDisplayModeId;
+ float renderFrameRate;
int[] supportedColorModes;
int activeColorMode;
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 3c220fc..488a148 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -36,11 +36,11 @@
import android.gui.IRegionSamplingListener;
import android.gui.IScreenCaptureListener;
import android.gui.ISurfaceComposerClient;
-import android.gui.ITransactionTraceListener;
import android.gui.ITunnelModeEnabledListener;
import android.gui.IWindowInfosListener;
import android.gui.LayerCaptureArgs;
import android.gui.LayerDebugInfo;
+import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
import android.gui.StaticDisplayInfo;
@@ -126,12 +126,14 @@
/**
* Gets immutable information about given physical display.
*/
- StaticDisplayInfo getStaticDisplayInfo(IBinder display);
+ StaticDisplayInfo getStaticDisplayInfo(long displayId);
/**
* Gets dynamic information about given physical display.
*/
- DynamicDisplayInfo getDynamicDisplayInfo(IBinder display);
+ DynamicDisplayInfo getDynamicDisplayInfoFromId(long displayId);
+
+ DynamicDisplayInfo getDynamicDisplayInfoFromToken(IBinder display);
DisplayPrimaries getDisplayNativePrimaries(IBinder display);
@@ -225,10 +227,6 @@
*/
PullAtomData onPullAtom(int atomId);
- oneway void enableVSyncInjections(boolean enable);
-
- oneway void injectVSync(long when);
-
/**
* Gets the list of active layers in Z order for debugging purposes
*
@@ -331,25 +329,9 @@
/**
* Sets the refresh rate boundaries for the display.
*
- * The primary refresh rate range represents display manager's general guidance on the display
- * modes we'll consider when switching refresh rates. Unless we get an explicit signal from an
- * app, we should stay within this range.
- *
- * The app request refresh rate range allows us to consider more display modes when switching
- * refresh rates. Although we should generally stay within the primary range, specific
- * considerations, such as layer frame rate settings specified via the setFrameRate() api, may
- * cause us to go outside the primary range. We never go outside the app request range. The app
- * request range will be greater than or equal to the primary refresh rate range, never smaller.
- *
- * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider
- * switching between. Only modes with a mode group and resolution matching defaultMode
- * will be considered for switching. The defaultMode corresponds to an ID of mode in the list
- * of supported modes returned from getDynamicDisplayInfo().
+ * @see DisplayModeSpecs.aidl for details.
*/
- void setDesiredDisplayModeSpecs(
- IBinder displayToken, int defaultMode,
- boolean allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax,
- float appRequestRefreshRateMin, float appRequestRefreshRateMax);
+ void setDesiredDisplayModeSpecs(IBinder displayToken, in DisplayModeSpecs specs);
DisplayModeSpecs getDesiredDisplayModeSpecs(IBinder displayToken);
@@ -453,11 +435,6 @@
void setOverrideFrameRate(int uid, float frameRate);
/**
- * Adds a TransactionTraceListener to listen for transaction tracing state updates.
- */
- void addTransactionTraceListener(ITransactionTraceListener listener);
-
- /**
* Gets priority of the RenderEngine in SurfaceFlinger.
*/
int getGpuContextPriority();
@@ -482,4 +459,6 @@
void addWindowInfosListener(IWindowInfosListener windowInfosListener);
void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
+
+ OverlayProperties getOverlaySupport();
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
index b8ee4d7..68781ce 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
@@ -19,7 +19,6 @@
import android.gui.CreateSurfaceResult;
import android.gui.FrameStats;
import android.gui.LayerMetadata;
-import android.gui.MirrorSurfaceResult;
/** @hide */
interface ISurfaceComposerClient {
@@ -58,7 +57,7 @@
*/
FrameStats getLayerFrameStats(IBinder handle);
- MirrorSurfaceResult mirrorSurface(IBinder mirrorFromHandle);
+ CreateSurfaceResult mirrorSurface(IBinder mirrorFromHandle);
- MirrorSurfaceResult mirrorDisplay(long displayId);
+ CreateSurfaceResult mirrorDisplay(long displayId);
}
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
similarity index 60%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
index 9fac3e8..dde4d38 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
@@ -16,8 +16,16 @@
package android.gui;
+import android.gui.ListenerStats;
+import android.gui.ReleaseCallbackId;
+
/** @hide */
-parcelable MirrorSurfaceResult {
- IBinder handle;
- int layerId;
+oneway interface ITransactionCompletedListener {
+ void onTransactionCompleted(in ListenerStats stats);
+
+ void onReleaseBuffer(in ReleaseCallbackId callbackId,
+ in @nullable ParcelFileDescriptor releaseFenceFd,
+ int currentMaxAcquiredBufferCount);
+
+ void onTransactionQueueStalled(@utf8InCpp String name);
}
diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
deleted file mode 100644
index 5cd12fd..0000000
--- a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.gui;
-
-/** @hide */
-interface ITransactionTraceListener {
- void onToggled(boolean enabled);
-}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl
similarity index 87%
rename from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
rename to libs/gui/aidl/android/gui/ListenerStats.aidl
index 9fac3e8..63248b2 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/gui/aidl/android/gui/ListenerStats.aidl
@@ -16,8 +16,4 @@
package android.gui;
-/** @hide */
-parcelable MirrorSurfaceResult {
- IBinder handle;
- int layerId;
-}
+parcelable ListenerStats cpp_header "gui/ListenerStats.h";
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
similarity index 77%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/gui/aidl/android/gui/OverlayProperties.aidl
index 9fac3e8..75cea15 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -17,7 +17,10 @@
package android.gui;
/** @hide */
-parcelable MirrorSurfaceResult {
- IBinder handle;
- int layerId;
+parcelable OverlayProperties {
+ parcelable SupportedBufferCombinations {
+ int[] pixelFormats;
+ int[] dataspaces;
+ }
+ SupportedBufferCombinations[] combinations;
}
diff --git a/libs/gui/aidl/android/gui/PullAtomData.aidl b/libs/gui/aidl/android/gui/PullAtomData.aidl
index 14d33c6..c307cef 100644
--- a/libs/gui/aidl/android/gui/PullAtomData.aidl
+++ b/libs/gui/aidl/android/gui/PullAtomData.aidl
@@ -18,6 +18,6 @@
/** @hide */
parcelable PullAtomData {
- @utf8InCpp String data;
+ byte[] data;
boolean success;
}
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
similarity index 87%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
index 9fac3e8..c86de34 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
@@ -16,8 +16,4 @@
package android.gui;
-/** @hide */
-parcelable MirrorSurfaceResult {
- IBinder handle;
- int layerId;
-}
+parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index cdc9376..82e1b5a 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -46,7 +46,7 @@
"android.hardware.configstore-utils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V4-cpp",
"android.hidl.token@1.0",
"libSurfaceFlingerProp",
"libgui",
@@ -62,7 +62,6 @@
"libutils",
"libnativewindow",
"libvndksupport",
- "libbufferhubqueue",
],
header_libs: [
"libdvr_headers",
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
index c97770b..761f08f 100644
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <android-base/stringprintf.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
@@ -91,8 +92,10 @@
const sp<FakeBnSurfaceComposerClient> testClient(new FakeBnSurfaceComposerClient());
sp<SurfaceComposerClient> client = new SurfaceComposerClient(testClient);
sp<BnGraphicBufferProducer> producer;
- return sp<SurfaceControl>::make(client, handle, mFdp.ConsumeIntegral<int32_t>(),
- mFdp.ConsumeIntegral<uint32_t>(),
+ uint32_t layerId = mFdp.ConsumeIntegral<uint32_t>();
+ std::string layerName = base::StringPrintf("#%d", layerId);
+ return sp<SurfaceControl>::make(client, handle, layerId, layerName,
+ mFdp.ConsumeIntegral<int32_t>(),
mFdp.ConsumeIntegral<uint32_t>(),
mFdp.ConsumeIntegral<int32_t>(),
mFdp.ConsumeIntegral<uint32_t>(),
@@ -145,7 +148,8 @@
sp<Fence> presentFence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
SurfaceControlStats controlStats(surface, mFdp.ConsumeIntegral<int64_t>(),
mFdp.ConsumeIntegral<int64_t>(), presentFence, previousFence,
- mFdp.ConsumeIntegral<uint32_t>(), frameStats);
+ mFdp.ConsumeIntegral<uint32_t>(), frameStats,
+ mFdp.ConsumeIntegral<uint32_t>());
stats.push_back(controlStats);
}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index d51f685..8810e4e 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -79,9 +79,11 @@
(override));
MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
(override));
- MOCK_METHOD(binder::Status, getStaticDisplayInfo, (const sp<IBinder>&, gui::StaticDisplayInfo*),
+ MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*),
(override));
- MOCK_METHOD(binder::Status, getDynamicDisplayInfo,
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*),
+ (override));
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken,
(const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
(const sp<IBinder>&, gui::DisplayPrimaries*), (override));
@@ -102,8 +104,6 @@
MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&),
(override));
MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override));
- MOCK_METHOD(binder::Status, enableVSyncInjections, (bool), (override));
- MOCK_METHOD(binder::Status, injectVSync, (int64_t), (override));
MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override));
MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override));
MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*),
@@ -129,9 +129,7 @@
MOCK_METHOD(binder::Status, removeTunnelModeEnabledListener,
(const sp<gui::ITunnelModeEnabledListener>&), (override));
MOCK_METHOD(binder::Status, setDesiredDisplayModeSpecs,
- (const sp<IBinder>&, int32_t, bool, float, float, float,
- float appRequestRefreshRateMax),
- (override));
+ (const sp<IBinder>&, const gui::DisplayModeSpecs&), (override));
MOCK_METHOD(binder::Status, getDesiredDisplayModeSpecs,
(const sp<IBinder>&, gui::DisplayModeSpecs*), (override));
MOCK_METHOD(binder::Status, getDisplayBrightnessSupport, (const sp<IBinder>&, bool*),
@@ -148,14 +146,13 @@
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, addTransactionTraceListener,
- (const sp<gui::ITransactionTraceListener>&), (override));
MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
MOCK_METHOD(binder::Status, addWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
+ MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
};
class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
@@ -171,11 +168,11 @@
(const sp<IBinder>& handle, gui::FrameStats* outStats), (override));
MOCK_METHOD(binder::Status, mirrorSurface,
- (const sp<IBinder>& mirrorFromHandle, gui::MirrorSurfaceResult* outResult),
+ (const sp<IBinder>& mirrorFromHandle, gui::CreateSurfaceResult* outResult),
(override));
MOCK_METHOD(binder::Status, mirrorDisplay,
- (int64_t displayId, gui::MirrorSurfaceResult* outResult), (override));
+ (int64_t displayId, gui::CreateSurfaceResult* outResult), (override));
};
class FakeDisplayEventDispatcher : public DisplayEventDispatcher {
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 48c90c5..57720dd 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -18,6 +18,7 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <libgui_fuzzer_utils.h>
+#include "android-base/stringprintf.h"
using namespace android;
@@ -122,10 +123,37 @@
sp<SurfaceControl> makeSurfaceControl();
BlurRegion getBlurRegion();
void fuzzOnPullAtom();
+ gui::DisplayModeSpecs getDisplayModeSpecs();
FuzzedDataProvider mFdp;
};
+gui::DisplayModeSpecs SurfaceComposerClientFuzzer::getDisplayModeSpecs() {
+ const auto getRefreshRateRange = [&] {
+ gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range;
+ range.min = mFdp.ConsumeFloatingPoint<float>();
+ range.max = mFdp.ConsumeFloatingPoint<float>();
+ return range;
+ };
+
+ const auto getRefreshRateRanges = [&] {
+ gui::DisplayModeSpecs::RefreshRateRanges ranges;
+ ranges.physical = getRefreshRateRange();
+ ranges.render = getRefreshRateRange();
+ return ranges;
+ };
+
+ String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
+ sp<IBinder> displayToken =
+ SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/);
+ gui::DisplayModeSpecs specs;
+ specs.defaultMode = mFdp.ConsumeIntegral<int32_t>();
+ specs.allowGroupSwitching = mFdp.ConsumeBool();
+ specs.primaryRanges = getRefreshRateRanges();
+ specs.appRequestRanges = getRefreshRateRanges();
+ return specs;
+}
+
BlurRegion SurfaceComposerClientFuzzer::getBlurRegion() {
int32_t left = mFdp.ConsumeIntegral<int32_t>();
int32_t right = mFdp.ConsumeIntegral<int32_t>();
@@ -175,7 +203,9 @@
uint32_t flags = mFdp.ConsumeIntegral<uint32_t>();
int32_t format = mFdp.ConsumeIntegral<int32_t>();
int32_t layerId = mFdp.ConsumeIntegral<int32_t>();
- return new SurfaceControl(client, handle, layerId, width, height, format, transformHint, flags);
+ std::string layerName = base::StringPrintf("#%d", layerId);
+ return new SurfaceControl(client, handle, layerId, layerName, width, height, format,
+ transformHint, flags);
}
void SurfaceComposerClientFuzzer::invokeSurfaceComposerTransaction() {
@@ -244,12 +274,7 @@
String8 displayName((mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes)).c_str());
sp<IBinder> displayToken =
SurfaceComposerClient::createDisplay(displayName, mFdp.ConsumeBool() /*secure*/);
- SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, mFdp.ConsumeIntegral<int32_t>(),
- mFdp.ConsumeBool() /*allowGroupSwitching*/,
- mFdp.ConsumeFloatingPoint<float>(),
- mFdp.ConsumeFloatingPoint<float>(),
- mFdp.ConsumeFloatingPoint<float>(),
- mFdp.ConsumeFloatingPoint<float>());
+ SurfaceComposerClient::setDesiredDisplayModeSpecs(displayToken, getDisplayModeSpecs());
ui::ColorMode colorMode = mFdp.PickValueInArray(kColormodes);
SurfaceComposerClient::setActiveColorMode(displayToken, colorMode);
@@ -268,10 +293,6 @@
sp<Surface> surfaceParent(
new Surface(producer, mFdp.ConsumeBool() /*controlledByApp*/, handle));
- SurfaceComposerClient::enableVSyncInjections(mFdp.ConsumeBool() /*secure*/);
- nsecs_t when = mFdp.ConsumeIntegral<uint32_t>();
- SurfaceComposerClient::injectVSync(when);
-
fuzzOnPullAtom();
SurfaceComposerClient::setDisplayContentSamplingEnabled(displayToken,
mFdp.ConsumeBool() /*enable*/,
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 4535c98..47dcc42 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -92,9 +92,13 @@
const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount);
+ void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount,
+ bool fakeRelease);
void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback,
bool acquireSingleBuffer = true);
void stopContinuousSyncTransaction();
+
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
void applyPendingTransactions(uint64_t frameNumber);
SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber);
@@ -110,12 +114,10 @@
uint64_t getLastAcquiredFrameNum();
/**
- * Set a callback to be invoked when we are hung. The boolean parameter
- * indicates whether the hang is due to an unfired fence.
- * TODO: The boolean is always true atm, unfired fence is
- * the only case we detect.
+ * Set a callback to be invoked when we are hung. The string parameter
+ * indicates the reason for the hang.
*/
- void setTransactionHangCallback(std::function<void(bool)> callback);
+ void setTransactionHangCallback(std::function<void(const std::string&)> callback);
virtual ~BLASTBufferQueue();
@@ -173,6 +175,12 @@
struct ReleasedBuffer {
ReleaseCallbackId callbackId;
sp<Fence> releaseFence;
+ bool operator==(const ReleasedBuffer& rhs) const {
+ // Only compare Id so if we somehow got two callbacks
+ // with different fences we don't decrement mNumAcquired
+ // too far.
+ return rhs.callbackId == callbackId;
+ }
};
std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex);
@@ -273,7 +281,7 @@
bool mAppliedLastTransaction = false;
uint64_t mLastAppliedFrameNumber = 0;
- std::function<void(bool)> mTransactionHangCallback;
+ std::function<void(const std::string&)> mTransactionHangCallback;
std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex);
};
diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h
deleted file mode 100644
index d380770..0000000
--- a/libs/gui/include/gui/BufferHubConsumer.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_BUFFERHUBCONSUMER_H_
-#define ANDROID_GUI_BUFFERHUBCONSUMER_H_
-
-#include <gui/IGraphicBufferConsumer.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-
-namespace android {
-
-class BufferHubConsumer : public IGraphicBufferConsumer {
-public:
- // Creates a BufferHubConsumer instance by importing an existing producer queue.
- static sp<BufferHubConsumer> Create(const std::shared_ptr<dvr::ConsumerQueue>& queue);
-
- // Creates a BufferHubConsumer instance by importing an existing producer
- // parcelable. Note that this call takes the ownership of the parcelable
- // object and is guaranteed to succeed if parcelable object is valid.
- static sp<BufferHubConsumer> Create(dvr::ConsumerQueueParcelable parcelable);
-
- // See |IGraphicBufferConsumer::acquireBuffer|
- status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) override;
-
- // See |IGraphicBufferConsumer::detachBuffer|
- status_t detachBuffer(int slot) override;
-
- // See |IGraphicBufferConsumer::attachBuffer|
- status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer) override;
-
- // See |IGraphicBufferConsumer::releaseBuffer|
- status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence,
- const sp<Fence>& releaseFence) override;
-
- // See |IGraphicBufferConsumer::consumerConnect|
- status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override;
-
- // See |IGraphicBufferConsumer::consumerDisconnect|
- status_t consumerDisconnect() override;
-
- // See |IGraphicBufferConsumer::getReleasedBuffers|
- status_t getReleasedBuffers(uint64_t* slotMask) override;
-
- // See |IGraphicBufferConsumer::setDefaultBufferSize|
- status_t setDefaultBufferSize(uint32_t w, uint32_t h) override;
-
- // See |IGraphicBufferConsumer::setMaxBufferCount|
- status_t setMaxBufferCount(int bufferCount) override;
-
- // See |IGraphicBufferConsumer::setMaxAcquiredBufferCount|
- status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) override;
-
- // See |IGraphicBufferConsumer::setConsumerName|
- status_t setConsumerName(const String8& name) override;
-
- // See |IGraphicBufferConsumer::setDefaultBufferFormat|
- status_t setDefaultBufferFormat(PixelFormat defaultFormat) override;
-
- // See |IGraphicBufferConsumer::setDefaultBufferDataSpace|
- status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) override;
-
- // See |IGraphicBufferConsumer::setConsumerUsageBits|
- status_t setConsumerUsageBits(uint64_t usage) override;
-
- // See |IGraphicBufferConsumer::setConsumerIsProtected|
- status_t setConsumerIsProtected(bool isProtected) override;
-
- // See |IGraphicBufferConsumer::setTransformHint|
- status_t setTransformHint(uint32_t hint) override;
-
- // See |IGraphicBufferConsumer::getSidebandStream|
- status_t getSidebandStream(sp<NativeHandle>* outStream) const override;
-
- // See |IGraphicBufferConsumer::getOccupancyHistory|
- status_t getOccupancyHistory(bool forceFlush,
- std::vector<OccupancyTracker::Segment>* outHistory) override;
-
- // See |IGraphicBufferConsumer::discardFreeBuffers|
- status_t discardFreeBuffers() override;
-
- // See |IGraphicBufferConsumer::dumpState|
- status_t dumpState(const String8& prefix, String8* outResult) const override;
-
- // BufferHubConsumer provides its own logic to cast to a binder object.
- IBinder* onAsBinder() override;
-
-private:
- // Private constructor to force use of |Create|.
- BufferHubConsumer() = default;
-
- // Concrete implementation backed by BufferHubBuffer.
- std::shared_ptr<dvr::ConsumerQueue> mQueue;
-};
-
-} // namespace android
-
-#endif // ANDROID_GUI_BUFFERHUBCONSUMER_H_
diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h
deleted file mode 100644
index 0e925ce..0000000
--- a/libs/gui/include/gui/BufferHubProducer.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GUI_BUFFERHUBPRODUCER_H_
-#define ANDROID_GUI_BUFFERHUBPRODUCER_H_
-
-#include <gui/BufferSlot.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-
-namespace android {
-
-class BufferHubProducer : public IGraphicBufferProducer {
-public:
- static constexpr int kNoConnectedApi = -1;
-
- // TODO(b/36187402) The actual implementation of BufferHubQueue's consumer
- // side logic doesn't limit the number of buffer it can acquire
- // simultaneously. We need a way for consumer logic to configure and enforce
- // that.
- static constexpr int kDefaultUndequeuedBuffers = 1;
-
- // Creates a BufferHubProducer instance by importing an existing prodcuer
- // queue.
- static sp<BufferHubProducer> Create(const std::shared_ptr<dvr::ProducerQueue>& producer);
-
- // Creates a BufferHubProducer instance by importing an existing prodcuer
- // parcelable. Note that this call takes the ownership of the parcelable
- // object and is guaranteed to succeed if parcelable object is valid.
- static sp<BufferHubProducer> Create(dvr::ProducerQueueParcelable parcelable);
-
- // See |IGraphicBufferProducer::requestBuffer|
- status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override;
-
- // For the BufferHub based implementation. All buffers in the queue are
- // allowed to be dequeued from the consumer side. It call always returns
- // 0 for |NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS| query. Thus setting
- // |max_dequeued_buffers| here can be considered the same as setting queue
- // capacity.
- //
- // See |IGraphicBufferProducer::setMaxDequeuedBufferCount| for more info
- status_t setMaxDequeuedBufferCount(int max_dequeued_buffers) override;
-
- // See |IGraphicBufferProducer::setAsyncMode|
- status_t setAsyncMode(bool async) override;
-
- // See |IGraphicBufferProducer::dequeueBuffer|
- status_t dequeueBuffer(int* out_slot, sp<Fence>* out_fence, uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
- FrameEventHistoryDelta* outTimestamps) override;
-
- // See |IGraphicBufferProducer::detachBuffer|
- status_t detachBuffer(int slot) override;
-
- // See |IGraphicBufferProducer::detachNextBuffer|
- status_t detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) override;
-
- // See |IGraphicBufferProducer::attachBuffer|
- status_t attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) override;
-
- // See |IGraphicBufferProducer::queueBuffer|
- status_t queueBuffer(int slot, const QueueBufferInput& input,
- QueueBufferOutput* output) override;
-
- // See |IGraphicBufferProducer::cancelBuffer|
- status_t cancelBuffer(int slot, const sp<Fence>& fence) override;
-
- // See |IGraphicBufferProducer::query|
- status_t query(int what, int* out_value) override;
-
- // See |IGraphicBufferProducer::connect|
- status_t connect(const sp<IProducerListener>& listener, int api,
- bool producer_controlled_by_app, QueueBufferOutput* output) override;
-
- // See |IGraphicBufferProducer::disconnect|
- status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) override;
-
- // See |IGraphicBufferProducer::setSidebandStream|
- status_t setSidebandStream(const sp<NativeHandle>& stream) override;
-
- // See |IGraphicBufferProducer::allocateBuffers|
- void allocateBuffers(uint32_t width, uint32_t height, PixelFormat format,
- uint64_t usage) override;
-
- // See |IGraphicBufferProducer::allowAllocation|
- status_t allowAllocation(bool allow) override;
-
- // See |IGraphicBufferProducer::setGenerationNumber|
- status_t setGenerationNumber(uint32_t generation_number) override;
-
- // See |IGraphicBufferProducer::getConsumerName|
- String8 getConsumerName() const override;
-
- // See |IGraphicBufferProducer::setSharedBufferMode|
- status_t setSharedBufferMode(bool shared_buffer_mode) override;
-
- // See |IGraphicBufferProducer::setAutoRefresh|
- status_t setAutoRefresh(bool auto_refresh) override;
-
- // See |IGraphicBufferProducer::setDequeueTimeout|
- status_t setDequeueTimeout(nsecs_t timeout) override;
-
- // See |IGraphicBufferProducer::getLastQueuedBuffer|
- status_t getLastQueuedBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence,
- float out_transform_matrix[16]) override;
-
- // See |IGraphicBufferProducer::getFrameTimestamps|
- void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) override;
-
- // See |IGraphicBufferProducer::getUniqueId|
- status_t getUniqueId(uint64_t* out_id) const override;
-
- // See |IGraphicBufferProducer::getConsumerUsage|
- status_t getConsumerUsage(uint64_t* out_usage) const override;
-
- // Takes out the current producer as a binder parcelable object. Note that the
- // producer must be disconnected to be exportable. After successful export,
- // the producer queue can no longer be connected again. Returns NO_ERROR when
- // takeout is successful and out_parcelable will hold the new parcelable
- // object. Also note that out_parcelable cannot be NULL and must points to an
- // invalid parcelable.
- status_t TakeAsParcelable(dvr::ProducerQueueParcelable* out_parcelable);
-
- IBinder* onAsBinder() override;
-
-protected:
- // See |IGraphicBufferProducer::exportToParcel|
- status_t exportToParcel(Parcel* parcel) override;
-
-private:
- using LocalHandle = pdx::LocalHandle;
-
- // Private constructor to force use of |Create|.
- BufferHubProducer() {}
-
- static uint64_t genUniqueId() {
- static std::atomic<uint32_t> counter{0};
- static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
- return id | counter++;
- }
-
- // Allocate new buffer through BufferHub and add it into |queue_| for
- // bookkeeping.
- status_t AllocateBuffer(uint32_t width, uint32_t height, uint32_t layer_count,
- PixelFormat format, uint64_t usage);
-
- // Remove a buffer via BufferHubRPC.
- status_t RemoveBuffer(size_t slot);
-
- // Free all buffers which are owned by the prodcuer. Note that if graphic
- // buffers are acquired by the consumer, we can't .
- status_t FreeAllBuffers();
-
- // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been
- // locked already.
- status_t DetachBufferLocked(size_t slot);
-
- // Concreate implementation backed by BufferHubBuffer.
- std::shared_ptr<dvr::ProducerQueue> queue_;
-
- // Mutex for thread safety.
- std::mutex mutex_;
-
- // Connect client API, should be one of the NATIVE_WINDOW_API_* flags.
- int connected_api_{kNoConnectedApi};
-
- // |max_buffer_count_| sets the capacity of the underlying buffer queue.
- int32_t max_buffer_count_{dvr::BufferHubQueue::kMaxQueueCapacity};
-
- // |max_dequeued_buffer_count_| set the maximum number of buffers that can
- // be dequeued at the same momment.
- int32_t max_dequeued_buffer_count_{1};
-
- // Sets how long dequeueBuffer or attachBuffer will block if a buffer or
- // slot is not yet available. The timeout is stored in milliseconds.
- int dequeue_timeout_ms_{dvr::BufferHubQueue::kNoTimeOut};
-
- // |generation_number_| stores the current generation number of the attached
- // producer. Any attempt to attach a buffer with a different generation
- // number will fail.
- // TOOD(b/38137191) Currently not used as we don't support
- // IGraphicBufferProducer::detachBuffer.
- uint32_t generation_number_{0};
-
- // |buffers_| stores the buffers that have been dequeued from
- // |dvr::BufferHubQueue|, It is initialized to invalid buffers, and gets
- // filled in with the result of |Dequeue|.
- // TODO(jwcai) The buffer allocated to a slot will also be replaced if the
- // requested buffer usage or geometry differs from that of the buffer
- // allocated to a slot.
- struct BufferHubSlot : public BufferSlot {
- BufferHubSlot() : mProducerBuffer(nullptr), mIsReallocating(false) {}
- // BufferSlot comes from android framework, using m prefix to comply with
- // the name convention with the reset of data fields from BufferSlot.
- std::shared_ptr<dvr::ProducerBuffer> mProducerBuffer;
- bool mIsReallocating;
- };
- BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity];
-
- // A uniqueId used by IGraphicBufferProducer interface.
- const uint64_t unique_id_{genUniqueId()};
-
- // A pending parcelable object which keeps the bufferhub channel alive.
- dvr::ProducerQueueParcelable pending_producer_parcelable_;
-};
-
-} // namespace android
-
-#endif // ANDROID_GUI_BUFFERHUBPRODUCER_H_
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index 91f80d2..690587f 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -82,12 +82,6 @@
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger = false);
-#ifndef NO_BUFFERHUB
- // Creates an IGraphicBufferProducer and IGraphicBufferConsumer pair backed by BufferHub.
- static void createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer,
- sp<IGraphicBufferConsumer>* outConsumer);
-#endif
-
BufferQueue() = delete; // Create through createBufferQueue
};
diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h
index 74f33a2..42b62c7 100644
--- a/libs/gui/include/gui/DisplayInfo.h
+++ b/libs/gui/include/gui/DisplayInfo.h
@@ -41,6 +41,8 @@
status_t writeToParcel(android::Parcel*) const override;
status_t readFromParcel(const android::Parcel*) override;
+
+ void dump(std::string& result, const char* prefix = "") const;
};
} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 1e85131..d70a7f0 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,12 +23,11 @@
#include <android/gui/IHdrLayerInfoListener.h>
#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
-#include <android/gui/ITransactionTraceListener.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/SpHash.h>
#include <math/vec4.h>
#include <stdint.h>
@@ -56,7 +55,7 @@
namespace android {
struct client_cache_t;
-struct ComposerState;
+class ComposerState;
struct DisplayStatInfo;
struct DisplayState;
struct InputWindowCommands;
@@ -67,6 +66,7 @@
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
+using gui::ListenerCallbacks;
using gui::SpHash;
namespace gui {
@@ -95,7 +95,6 @@
// flags for setTransactionState()
enum {
- eSynchronous = 0x01,
eAnimation = 0x02,
// Explicit indication that this transaction and others to follow will likely result in a
@@ -112,7 +111,7 @@
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state,
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 5af5989..9cf62bc 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -30,7 +30,8 @@
METADATA_ACCESSIBILITY_ID = 5,
METADATA_OWNER_PID = 6,
METADATA_DEQUEUE_TIME = 7,
- METADATA_GAME_MODE = 8
+ METADATA_GAME_MODE = 8,
+ METADATA_CALLING_UID = 9,
};
struct LayerMetadata : public Parcelable {
@@ -65,8 +66,9 @@
Standard = 1,
Performance = 2,
Battery = 3,
+ Custom = 4,
- ftl_last = Battery
+ ftl_last = Custom
};
} // namespace android::gui
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index c8927ad..c5fdf82 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,10 +21,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#include <android/gui/DropInputMode.h>
@@ -35,6 +35,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfo.h>
@@ -56,6 +57,9 @@
using gui::ISurfaceComposerClient;
using gui::LayerMetadata;
+using gui::ITransactionCompletedListener;
+using gui::ReleaseCallbackId;
+
struct client_cache_t {
wp<IBinder> token = nullptr;
uint64_t id;
@@ -148,12 +152,14 @@
enum {
ePositionChanged = 0x00000001,
eLayerChanged = 0x00000002,
- // unused = 0x00000004,
+ /* unused = 0x00000004, */
eAlphaChanged = 0x00000008,
eMatrixChanged = 0x00000010,
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
+ /* unused = 0x00000100, */
+ /* unused = 0x00000200, */
eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
eRenderBorderChanged = 0x00001000,
@@ -161,8 +167,8 @@
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
eColorChanged = 0x00010000,
- eDestroySurface = 0x00020000,
- eTransformChanged = 0x00040000,
+ /* unused = 0x00020000, */
+ eBufferTransformChanged = 0x00040000,
eTransformToDisplayInverseChanged = 0x00080000,
eCropChanged = 0x00100000,
eBufferChanged = 0x00200000,
@@ -199,7 +205,32 @@
void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ // Compares two layer_state_t structs and returns a set of change flags describing all the
+ // states that are different.
+ uint64_t diff(const layer_state_t& other) const;
bool hasBufferChanges() const;
+
+ // Changes to the tree structure.
+ static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent |
+ layer_state_t::eBackgroundColorChanged;
+ // Content updates.
+ static constexpr uint64_t CONTENT_CHANGES = layer_state_t::eAlphaChanged |
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eRenderBorderChanged | layer_state_t::eColorChanged |
+ layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged | layer_state_t::eSidebandStreamChanged |
+ layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eBackgroundColorChanged | layer_state_t::eColorSpaceAgnosticChanged |
+ layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eAutoRefreshChanged | layer_state_t::eStretchChanged;
+ // Changes to content or children size.
+ static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::ePositionChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::eTransparentRegionChanged |
+ layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDestinationFrameChanged;
+
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -210,6 +241,11 @@
float dsdy{0};
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ inline bool operator==(const matrix22_t& other) const {
+ return std::tie(dsdx, dtdx, dtdy, dsdy) ==
+ std::tie(other.dsdx, other.dtdx, other.dtdy, other.dsdy);
+ }
+ inline bool operator!=(const matrix22_t& other) const { return !(*this == other); }
};
sp<IBinder> surface;
int32_t layerId;
@@ -218,25 +254,22 @@
float y;
int32_t z;
ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
- float alpha;
uint32_t flags;
uint32_t mask;
uint8_t reserved;
matrix22_t matrix;
float cornerRadius;
uint32_t backgroundBlurRadius;
- sp<SurfaceControl> reparentSurfaceControl;
sp<SurfaceControl> relativeLayerSurfaceControl;
sp<SurfaceControl> parentSurfaceControlForChild;
- half3 color;
+ half4 color;
// non POD must be last. see write/read
Region transparentRegion;
-
- uint32_t transform;
+ uint32_t bufferTransform;
bool transformToDisplayInverse;
Rect crop;
std::shared_ptr<BufferData> bufferData = nullptr;
@@ -312,7 +345,8 @@
bool dimmingEnabled;
};
-struct ComposerState {
+class ComposerState {
+public:
layer_state_t state;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
@@ -329,6 +363,7 @@
DisplayState();
void merge(const DisplayState& other);
+ void sanitize(int32_t permissions);
uint32_t what = 0;
uint32_t flags = 0;
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ListenerStats.h
similarity index 80%
rename from libs/gui/include/gui/ITransactionCompletedListener.h
rename to libs/gui/include/gui/ListenerStats.h
index cc136bb..3a12802 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ListenerStats.h
@@ -24,6 +24,8 @@
#include <binder/SafeInterface.h>
#include <gui/FrameTimestamps.h>
+#include <gui/ReleaseCallbackId.h>
+
#include <ui/Fence.h>
#include <utils/Timers.h>
@@ -32,10 +34,7 @@
#include <unordered_set>
#include <variant>
-namespace android {
-
-class ITransactionCompletedListener;
-class ListenerCallbacks;
+namespace android::gui {
class CallbackId : public Parcelable {
public:
@@ -54,30 +53,6 @@
std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
};
-class ReleaseCallbackId : public Parcelable {
-public:
- static const ReleaseCallbackId INVALID_ID;
-
- uint64_t bufferId;
- uint64_t framenumber;
- ReleaseCallbackId() {}
- ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
- : bufferId(bufferId), framenumber(framenumber) {}
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-
- bool operator==(const ReleaseCallbackId& rhs) const {
- return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
- }
- bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
- std::string to_string() const {
- if (*this == INVALID_ID) return "INVALID_ID";
-
- return "bufferId:" + std::to_string(bufferId) +
- " framenumber:" + std::to_string(framenumber);
- }
-};
-
struct ReleaseBufferCallbackIdHash {
std::size_t operator()(const ReleaseCallbackId& key) const {
return std::hash<uint64_t>()(key.bufferId);
@@ -132,7 +107,7 @@
SurfaceStats() = default;
SurfaceStats(const sp<IBinder>& sc, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
- const sp<Fence>& prevReleaseFence, uint32_t hint,
+ const sp<Fence>& prevReleaseFence, std::optional<uint32_t> hint,
uint32_t currentMaxAcquiredBuffersCount, FrameEventHistoryStats frameEventStats,
std::vector<JankData> jankData, ReleaseCallbackId previousReleaseCallbackId)
: surfaceControl(sc),
@@ -147,7 +122,7 @@
sp<IBinder> surfaceControl;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
sp<Fence> previousReleaseFence;
- uint32_t transformHint = 0;
+ std::optional<uint32_t> transformHint = 0;
uint32_t currentMaxAcquiredBufferCount = 0;
FrameEventHistoryStats eventStats;
std::vector<JankData> jankData;
@@ -186,26 +161,6 @@
std::vector<TransactionStats> transactionStats;
};
-class ITransactionCompletedListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(TransactionCompletedListener)
-
- virtual void onTransactionCompleted(ListenerStats stats) = 0;
-
- virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) = 0;
- virtual void onTransactionQueueStalled() = 0;
-};
-
-class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
-public:
- BnTransactionCompletedListener()
- : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
class ListenerCallbacks {
public:
ListenerCallbacks(const sp<IBinder>& listener,
@@ -267,4 +222,4 @@
}
};
-} // namespace android
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h
new file mode 100644
index 0000000..142ee5a
--- /dev/null
+++ b/libs/gui/include/gui/ReleaseCallbackId.h
@@ -0,0 +1,50 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <cstdint>
+
+namespace android::gui {
+
+class ReleaseCallbackId : public Parcelable {
+public:
+ static const ReleaseCallbackId INVALID_ID;
+
+ uint64_t bufferId;
+ uint64_t framenumber;
+ ReleaseCallbackId() {}
+ ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+ : bufferId(bufferId), framenumber(framenumber) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const ReleaseCallbackId& rhs) const {
+ return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+ }
+ bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+ std::string to_string() const {
+ if (*this == INVALID_ID) return "INVALID_ID";
+
+ return "bufferId:" + std::to_string(bufferId) +
+ " framenumber:" + std::to_string(framenumber);
+ }
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 634acb6..b9ccdc9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -113,6 +113,24 @@
return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr;
}
+ static sp<IGraphicBufferProducer> getIGraphicBufferProducer(ANativeWindow* window) {
+ int val;
+ if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 &&
+ val == NATIVE_WINDOW_SURFACE) {
+ return ((Surface*) window)->mGraphicBufferProducer;
+ }
+ return nullptr;
+ }
+
+ static sp<IBinder> getSurfaceControlHandle(ANativeWindow* window) {
+ int val;
+ if (window->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &val) >= 0 &&
+ val == NATIVE_WINDOW_SURFACE) {
+ return ((Surface*) window)->mSurfaceControlHandle;
+ }
+ return nullptr;
+ }
+
/* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer.
*
* A sideband stream is a device-specific mechanism for passing buffers
@@ -187,8 +205,8 @@
nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime,
nsecs_t* outReleaseTime);
- status_t getWideColorSupport(bool* supported);
- status_t getHdrSupport(bool* supported);
+ status_t getWideColorSupport(bool* supported) __attribute__((__deprecated__));
+ status_t getHdrSupport(bool* supported) __attribute__((__deprecated__));
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
@@ -468,6 +486,11 @@
// queue operation. There is no HDR metadata by default.
HdrMetadata mHdrMetadata;
+ // mHdrMetadataIsSet is a bitfield to track which HDR metadata has been set.
+ // Prevent Surface from resetting HDR metadata that was set on a bufer when
+ // HDR metadata is not set on this Surface.
+ uint32_t mHdrMetadataIsSet{0};
+
// mCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mCrop;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 61d4714..96d3a23 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,10 +42,13 @@
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/BnTransactionCompletedListener.h>
+
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -59,32 +62,45 @@
class ITunnelModeEnabledListener;
class Region;
+using gui::BnTransactionCompletedListener;
+using gui::CallbackId;
+using gui::CallbackIdHash;
using gui::DisplayCaptureArgs;
+using gui::FrameEventHistoryStats;
using gui::IRegionSamplingListener;
using gui::ISurfaceComposerClient;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
using gui::LayerCaptureArgs;
using gui::LayerMetadata;
+using gui::ListenerStats;
+using gui::ReleaseBufferCallbackIdHash;
+using gui::ReleaseCallbackId;
+using gui::SurfaceStats;
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
- uint32_t hint, FrameEventHistoryStats eventStats)
+ std::optional<uint32_t> hint, FrameEventHistoryStats eventStats,
+ uint32_t currentMaxAcquiredBufferCount)
: surfaceControl(sc),
latchTime(latchTime),
acquireTimeOrFence(std::move(acquireTimeOrFence)),
presentFence(presentFence),
previousReleaseFence(prevReleaseFence),
transformHint(hint),
- frameEventStats(eventStats) {}
+ frameEventStats(eventStats),
+ currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {}
sp<SurfaceControl> surfaceControl;
nsecs_t latchTime = -1;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
sp<Fence> presentFence;
sp<Fence> previousReleaseFence;
- uint32_t transformHint = 0;
+ std::optional<uint32_t> transformHint = 0;
FrameEventHistoryStats frameEventStats;
+ uint32_t currentMaxAcquiredBufferCount = 0;
};
using TransactionCompletedCallbackTakesContext =
@@ -146,28 +162,21 @@
static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
// Get immutable information about given physical display.
- static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*);
+ static status_t getStaticDisplayInfo(int64_t, ui::StaticDisplayInfo*);
- // Get dynamic information about given physical display.
- static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*);
+ // Get dynamic information about given physical display from display id
+ static status_t getDynamicDisplayInfoFromId(int64_t, ui::DynamicDisplayInfo*);
// Shorthand for the active display mode from getDynamicDisplayInfo().
// TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function.
static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*);
// Sets the refresh rate boundaries for the display.
- static status_t setDesiredDisplayModeSpecs(
- const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
- bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax,
- float appRequestRefreshRateMin, float appRequestRefreshRateMax);
+ static status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ const gui::DisplayModeSpecs&);
// Gets the refresh rate boundaries for the display.
static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
- ui::DisplayModeId* outDefaultMode,
- bool* outAllowGroupSwitching,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax);
+ gui::DisplayModeSpecs*);
// Get the coordinates of the display's native color primaries
static status_t getDisplayNativePrimaries(const sp<IBinder>& display,
@@ -179,6 +188,10 @@
// Gets if boot display mode operations are supported on a device
static status_t getBootDisplayModeSupport(bool* support);
+
+ // Gets the overlay properties of the device
+ static status_t getOverlaySupport(gui::OverlayProperties* outProperties);
+
// Sets the user-preferred display mode that a device should boot in
static status_t setBootDisplayMode(const sp<IBinder>& display, ui::DisplayModeId);
// Clears the user-preferred display mode
@@ -351,15 +364,9 @@
//! Get stable IDs for connected physical displays
static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
- static std::optional<PhysicalDisplayId> getInternalDisplayId();
//! Get token for a physical display given its stable ID
static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
- static sp<IBinder> getInternalDisplayToken();
-
- static status_t enableVSyncInjections(bool enable);
-
- static status_t injectVSync(nsecs_t when);
struct SCHash {
std::size_t operator()(const sp<SurfaceControl>& sc) const {
@@ -720,6 +727,12 @@
ReleaseCallbackThread mReleaseCallbackThread;
private:
+ // Get dynamic information about given physical display from token
+ static status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo*);
+
+ static void getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo);
virtual void onFirstRef();
mutable Mutex mLock;
@@ -785,7 +798,7 @@
// This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
// std::recursive_mutex
std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
- std::unordered_map<void*, std::function<void()>> mQueueStallListeners;
+ std::unordered_map<void*, std::function<void(const std::string&)>> mQueueStallListeners;
public:
static sp<TransactionCompletedListener> getInstance();
@@ -803,7 +816,7 @@
const sp<SurfaceControl>& surfaceControl,
const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
- void addQueueStallListener(std::function<void()> stallListener, void* id);
+ void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id);
void removeQueueStallListener(void *id);
/*
@@ -825,17 +838,17 @@
void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
// BnTransactionCompletedListener overrides
- void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionCompleted(const ListenerStats& stats) override;
+ binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionQueueStalled(const std::string& reason) override;
void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
- void onTransactionQueueStalled() override;
-
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index e4a1350..1d4fc7f 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -78,6 +78,7 @@
sp<IBinder> getHandle() const;
sp<IBinder> getLayerStateHandle() const;
int32_t getLayerId() const;
+ const std::string& getName() const;
sp<IGraphicBufferProducer> getIGraphicBufferProducer();
@@ -94,8 +95,9 @@
explicit SurfaceControl(const sp<SurfaceControl>& other);
SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- int32_t layerId, uint32_t width = 0, uint32_t height = 0, PixelFormat format = 0,
- uint32_t transformHint = 0, uint32_t flags = 0);
+ int32_t layerId, const std::string& layerName, uint32_t width = 0,
+ uint32_t height = 0, PixelFormat format = 0, uint32_t transformHint = 0,
+ uint32_t flags = 0);
sp<SurfaceControl> getParentingLayer();
@@ -121,6 +123,7 @@
mutable sp<BLASTBufferQueue> mBbq;
mutable sp<SurfaceControl> mBbqChild;
int32_t mLayerId = 0;
+ std::string mName;
uint32_t mTransformHint = 0;
uint32_t mWidth = 0;
uint32_t mHeight = 0;
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index 4c01683..441b833 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -30,6 +30,12 @@
#define ATRACE_FORMAT_INSTANT(fmt, ...) \
(CC_UNLIKELY(ATRACE_ENABLED()) && (TraceUtils::instantFormat(fmt, ##__VA_ARGS__), true))
+#define ALOGE_AND_TRACE(fmt, ...) \
+ do { \
+ ALOGE(fmt, ##__VA_ARGS__); \
+ ATRACE_FORMAT_INSTANT(fmt, ##__VA_ARGS__); \
+ } while (false)
+
namespace android {
class TraceUtils {
diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h
deleted file mode 100644
index 9efba47..0000000
--- a/libs/gui/include/gui/TransactionTracing.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2020 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/gui/BnTransactionTraceListener.h>
-#include <utils/Mutex.h>
-
-namespace android {
-
-class TransactionTraceListener : public gui::BnTransactionTraceListener {
- static std::mutex sMutex;
- static sp<TransactionTraceListener> sInstance;
-
- TransactionTraceListener();
-
-public:
- static sp<TransactionTraceListener> getInstance();
-
- binder::Status onToggled(bool enabled) override;
-
- bool isTracingEnabled();
-
-private:
- bool mTracingEnabled = false;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index ac74c8a..b01a3db 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -272,6 +272,7 @@
WindowInfoHandle(const WindowInfo& other);
inline const WindowInfo* getInfo() const { return &mInfo; }
+ inline WindowInfo* editInfo() { return &mInfo; }
sp<IBinder> getToken() const;
diff --git a/libs/gui/include/gui/fake/BufferData.h b/libs/gui/include/gui/fake/BufferData.h
new file mode 100644
index 0000000..725d11c
--- /dev/null
+++ b/libs/gui/include/gui/fake/BufferData.h
@@ -0,0 +1,51 @@
+/*
+ * 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 <gui/LayerState.h>
+
+namespace android::fake {
+
+// Class which exposes buffer properties from BufferData without holding on to an actual buffer
+class BufferData : public android::BufferData {
+public:
+ BufferData(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
+ uint64_t outUsage)
+ : mBufferId(bufferId),
+ mWidth(width),
+ mHeight(height),
+ mPixelFormat(pixelFormat),
+ mOutUsage(outUsage) {}
+ bool hasBuffer() const override { return mBufferId != 0; }
+ bool hasSameBuffer(const android::BufferData& other) const override {
+ return getId() == other.getId() && frameNumber == other.frameNumber;
+ }
+ uint32_t getWidth() const override { return mWidth; }
+ uint32_t getHeight() const override { return mHeight; }
+ uint64_t getId() const override { return mBufferId; }
+ PixelFormat getPixelFormat() const override { return mPixelFormat; }
+ uint64_t getUsage() const override { return mOutUsage; }
+
+private:
+ uint64_t mBufferId;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ int32_t mPixelFormat;
+ uint64_t mOutUsage;
+};
+
+} // namespace android::fake
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 62d1496..08785b4 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -135,7 +135,8 @@
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
nsecs_t latchTime) const {
const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
- previousReleaseFence, transformHint, frameEvents] = surfaceControlStats;
+ previousReleaseFence, transformHint, frameEvents, ignore] =
+ surfaceControlStats;
ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
diff --git a/libs/gui/include/private/gui/ComposerServiceAIDL.h b/libs/gui/include/private/gui/ComposerServiceAIDL.h
index 2963583..6352a58 100644
--- a/libs/gui/include/private/gui/ComposerServiceAIDL.h
+++ b/libs/gui/include/private/gui/ComposerServiceAIDL.h
@@ -51,28 +51,6 @@
// Get a connection to the Composer Service. This will block until
// a connection is established. Returns null if permission is denied.
static sp<gui::ISurfaceComposer> getComposerService();
-
- // the following two methods are moved from ISurfaceComposer.h
- // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
- std::optional<PhysicalDisplayId> getInternalDisplayId() const {
- std::vector<int64_t> displayIds;
- binder::Status status = mComposerService->getPhysicalDisplayIds(&displayIds);
- return (!status.isOk() || displayIds.empty())
- ? std::nullopt
- : DisplayId::fromValue<PhysicalDisplayId>(
- static_cast<uint64_t>(displayIds.front()));
- }
-
- // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic.
- sp<IBinder> getInternalDisplayToken() const {
- const auto displayId = getInternalDisplayId();
- if (!displayId) return nullptr;
- sp<IBinder> display;
- binder::Status status =
- mComposerService->getPhysicalDisplayToken(static_cast<int64_t>(displayId->value),
- &display);
- return status.isOk() ? display : nullptr;
- }
};
// ---------------------------------------------------------------------------
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 7b37b25..183acc1 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -93,39 +93,6 @@
header_libs: ["libsurfaceflinger_headers"],
}
-// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
-// This test has a main method, and requires a separate binary to be built.
-// To add move tests like this, just add additional cc_test statements,
-// as opposed to adding more source files to this one.
-cc_test {
- name: "SurfaceParcelable_test",
- test_suites: ["device-tests"],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- srcs: [
- "SurfaceParcelable_test.cpp",
- ],
-
- shared_libs: [
- "liblog",
- "libbinder",
- "libcutils",
- "libgui",
- "libui",
- "libutils",
- "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
- "libpdx_default_transport",
- ],
-
- header_libs: [
- "libdvr_headers",
- ],
-}
-
cc_test {
name: "SamplingDemo",
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index c4c2fa5..cf2593d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -188,7 +188,10 @@
void SetUp() {
mComposer = ComposerService::getComposerService();
mClient = new SurfaceComposerClient();
- mDisplayToken = mClient->getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ // display 0 is picked as this test is not much display depedent
+ mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_NE(nullptr, mDisplayToken.get());
Transaction t;
t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index b647aab..0a2750a 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -32,7 +32,10 @@
void SetUp() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(OK, mComposerClient->initCheck());
- mDisplayToken = mComposerClient->getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ // display 0 is picked for now, can extend to support all displays if needed
+ mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_TRUE(mDisplayToken);
}
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 2637f59..3344e0b 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -360,8 +360,10 @@
void SetUp() {
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- const auto display = mComposerClient->getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ // display 0 is picked for now, can extend to support all displays if needed
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_NE(display, nullptr);
ui::DisplayMode mode;
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 2af2fe1..3427731 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -42,10 +42,6 @@
#define TEST_CONTROLLED_BY_APP false
#define TEST_PRODUCER_USAGE_BITS (0)
-#ifndef USE_BUFFER_HUB_AS_BUFFER_QUEUE
-#define USE_BUFFER_HUB_AS_BUFFER_QUEUE 0
-#endif
-
namespace android {
namespace {
@@ -77,7 +73,6 @@
// Enums to control which IGraphicBufferProducer backend to test.
enum IGraphicBufferProducerTestCode {
USE_BUFFER_QUEUE_PRODUCER = 0,
- USE_BUFFER_HUB_PRODUCER,
};
}; // namespace anonymous
@@ -99,10 +94,6 @@
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
break;
}
- case USE_BUFFER_HUB_PRODUCER: {
- BufferQueue::createBufferHubQueue(&mProducer, &mConsumer);
- break;
- }
default: {
// Should never reach here.
LOG_ALWAYS_FATAL("Invalid test params: %u", GetParam());
@@ -880,11 +871,6 @@
}
TEST_P(IGraphicBufferProducerTest, SetAsyncMode_Succeeds) {
- if (GetParam() == USE_BUFFER_HUB_PRODUCER) {
- // TODO(b/36724099): Add support for BufferHubProducer::setAsyncMode(true)
- return;
- }
-
ASSERT_OK(mConsumer->setMaxAcquiredBufferCount(1)) << "maxAcquire: " << 1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_OK(mProducer->setAsyncMode(true)) << "async mode: " << true;
@@ -1117,14 +1103,7 @@
}
}
-#if USE_BUFFER_HUB_AS_BUFFER_QUEUE
-INSTANTIATE_TEST_CASE_P(IGraphicBufferProducerBackends, IGraphicBufferProducerTest,
- ::testing::Values(USE_BUFFER_QUEUE_PRODUCER, USE_BUFFER_HUB_PRODUCER));
-#else
-// TODO(b/70046255): Remove the #ifdef here and always tests both backends once BufferHubQueue can
-// pass all existing libgui tests.
INSTANTIATE_TEST_CASE_P(IGraphicBufferProducerBackends, IGraphicBufferProducerTest,
::testing::Values(USE_BUFFER_QUEUE_PRODUCER));
-#endif
} // namespace android
diff --git a/libs/gui/tests/SurfaceParcelable_test.cpp b/libs/gui/tests/SurfaceParcelable_test.cpp
deleted file mode 100644
index 686dc82..0000000
--- a/libs/gui/tests/SurfaceParcelable_test.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2018 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 "SurfaceParcelable_test"
-
-#include <gtest/gtest.h>
-
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <gui/BufferHubProducer.h>
-#include <gui/BufferQueue.h>
-#include <gui/view/Surface.h>
-#include <utils/Log.h>
-
-namespace android {
-
-static const String16 kTestServiceName = String16("SurfaceParcelableTestService");
-static const String16 kSurfaceName = String16("TEST_SURFACE");
-static const uint32_t kBufferWidth = 100;
-static const uint32_t kBufferHeight = 1;
-static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-
-enum SurfaceParcelableTestServiceCode {
- CREATE_BUFFER_QUEUE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
- CREATE_BUFFER_HUB_SURFACE,
-};
-
-class SurfaceParcelableTestService : public BBinder {
-public:
- SurfaceParcelableTestService() {
- // BufferQueue
- BufferQueue::createBufferQueue(&mBufferQueueProducer, &mBufferQueueConsumer);
-
- // BufferHub
- dvr::ProducerQueueConfigBuilder configBuilder;
- mProducerQueue = dvr::ProducerQueue::Create(configBuilder.SetDefaultWidth(kBufferWidth)
- .SetDefaultHeight(kBufferHeight)
- .SetDefaultFormat(kBufferFormat)
- .Build(),
- dvr::UsagePolicy{});
- mBufferHubProducer = BufferHubProducer::Create(mProducerQueue);
- }
-
- ~SurfaceParcelableTestService() = default;
-
- virtual status_t onTransact(uint32_t code, const Parcel& /*data*/, Parcel* reply,
- uint32_t /*flags*/ = 0) {
- switch (code) {
- case CREATE_BUFFER_QUEUE_SURFACE: {
- view::Surface surfaceShim;
- surfaceShim.name = kSurfaceName;
- surfaceShim.graphicBufferProducer = mBufferQueueProducer;
- return surfaceShim.writeToParcel(reply);
- }
- case CREATE_BUFFER_HUB_SURFACE: {
- view::Surface surfaceShim;
- surfaceShim.name = kSurfaceName;
- surfaceShim.graphicBufferProducer = mBufferHubProducer;
- return surfaceShim.writeToParcel(reply);
- }
- default:
- return UNKNOWN_TRANSACTION;
- };
- }
-
-protected:
- sp<IGraphicBufferProducer> mBufferQueueProducer;
- sp<IGraphicBufferConsumer> mBufferQueueConsumer;
-
- std::shared_ptr<dvr::ProducerQueue> mProducerQueue;
- sp<IGraphicBufferProducer> mBufferHubProducer;
-};
-
-static int runBinderServer() {
- ProcessState::self()->startThreadPool();
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<SurfaceParcelableTestService> service = new SurfaceParcelableTestService;
- sm->addService(kTestServiceName, service, false);
-
- ALOGI("Binder server running...");
-
- while (true) {
- int stat, retval;
- retval = wait(&stat);
- if (retval == -1 && errno == ECHILD) {
- break;
- }
- }
-
- ALOGI("Binder server exiting...");
- return 0;
-}
-
-class SurfaceParcelableTest : public ::testing::TestWithParam<uint32_t> {
-protected:
- virtual void SetUp() {
- mService = defaultServiceManager()->getService(kTestServiceName);
- if (mService == nullptr) {
- ALOGE("Failed to connect to the test service.");
- return;
- }
-
- ALOGI("Binder service is ready for client.");
- }
-
- status_t GetSurface(view::Surface* surfaceShim) {
- ALOGI("...Test: %d", GetParam());
-
- uint32_t opCode = GetParam();
- Parcel data;
- Parcel reply;
- status_t error = mService->transact(opCode, data, &reply);
- if (error != NO_ERROR) {
- ALOGE("Failed to get surface over binder, error=%d.", error);
- return error;
- }
-
- error = surfaceShim->readFromParcel(&reply);
- if (error != NO_ERROR) {
- ALOGE("Failed to get surface from parcel, error=%d.", error);
- return error;
- }
-
- return NO_ERROR;
- }
-
-private:
- sp<IBinder> mService;
-};
-
-TEST_P(SurfaceParcelableTest, SendOverBinder) {
- view::Surface surfaceShim;
- EXPECT_EQ(GetSurface(&surfaceShim), NO_ERROR);
- EXPECT_EQ(surfaceShim.name, kSurfaceName);
- EXPECT_FALSE(surfaceShim.graphicBufferProducer == nullptr);
-}
-
-INSTANTIATE_TEST_CASE_P(SurfaceBackends, SurfaceParcelableTest,
- ::testing::Values(CREATE_BUFFER_QUEUE_SURFACE, CREATE_BUFFER_HUB_SURFACE));
-
-} // namespace android
-
-int main(int argc, char** argv) {
- pid_t pid = fork();
- if (pid == 0) {
- android::ProcessState::self()->startThreadPool();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-
- } else {
- ALOGI("Test process pid: %d.", pid);
- return android::runBinderServer();
- }
-}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index b9358e7..55242df 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -263,7 +263,10 @@
sp<ANativeWindow> anw(mSurface);
// Verify the screenshot works with no protected buffers.
- const sp<IBinder> display = ComposerServiceAIDL::getInstance().getInternalDisplayToken();
+ 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;
@@ -693,7 +696,7 @@
}
status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
- const Vector<ComposerState>& /*state*/,
+ Vector<ComposerState>& /*state*/,
const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
@@ -779,13 +782,18 @@
return binder::Status::ok();
}
- binder::Status getStaticDisplayInfo(const sp<IBinder>& /*display*/,
+ binder::Status getStaticDisplayInfo(int64_t /*displayId*/,
gui::StaticDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& /*display*/,
- gui::DynamicDisplayInfo* /*outInfo*/) override {
+ binder::Status getDynamicDisplayInfoFromId(int64_t /*displayId*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& /*display*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
@@ -848,10 +856,6 @@
return binder::Status::ok();
}
- binder::Status enableVSyncInjections(bool /*enable*/) override { return binder::Status::ok(); }
-
- binder::Status injectVSync(int64_t /*when*/) override { return binder::Status::ok(); }
-
binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* /*outLayers*/) override {
return binder::Status::ok();
}
@@ -921,16 +925,12 @@
}
binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
- int32_t /*defaultMode*/, bool /*allowGroupSwitching*/,
- float /*primaryRefreshRateMin*/,
- float /*primaryRefreshRateMax*/,
- float /*appRequestRefreshRateMin*/,
- float /*appRequestRefreshRateMax*/) override {
+ const gui::DisplayModeSpecs&) override {
return binder::Status::ok();
}
binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
- gui::DisplayModeSpecs* /*outSpecs*/) override {
+ gui::DisplayModeSpecs*) override {
return binder::Status::ok();
}
@@ -974,11 +974,6 @@
return binder::Status::ok();
}
- binder::Status addTransactionTraceListener(
- const sp<gui::ITransactionTraceListener>& /*listener*/) override {
- return binder::Status::ok();
- }
-
binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override {
return binder::Status::ok();
}
@@ -997,6 +992,10 @@
return binder::Status::ok();
}
+ binder::Status getOverlaySupport(gui::OverlayProperties* /*properties*/) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 29e02cf..34ef7b4 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -89,7 +89,6 @@
shared_libs: [
"libutils",
"libbinder",
- "libui",
],
static_libs: [
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 579b28e..9e8ebf3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -21,7 +21,9 @@
#include <cutils/compiler.h>
#include <inttypes.h>
#include <string.h>
+#include <optional>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
@@ -34,7 +36,7 @@
#ifdef __linux__
#include <binder/Parcel.h>
#endif
-#ifdef __ANDROID__
+#if defined(__ANDROID__)
#include <sys/random.h>
#endif
@@ -112,15 +114,31 @@
}
// --- IdGenerator ---
+#if defined(__ANDROID__)
+[[maybe_unused]]
+#endif
+static 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) {
+ return -errno;
+ }
+
+ base::unique_fd fd(ret);
+ if (!base::ReadFully(fd, data, size)) {
+ return -errno;
+ }
+ return OK;
+}
+
IdGenerator::IdGenerator(Source source) : mSource(source) {}
int32_t IdGenerator::nextId() const {
constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK;
int32_t id = 0;
-// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't
-// use sequence number so just always return mSource.
-#ifdef __ANDROID__
+#if defined(__ANDROID__)
+ // On device, prefer 'getrandom' to '/dev/urandom' because it's faster.
constexpr size_t BUF_LEN = sizeof(id);
size_t totalBytes = 0;
while (totalBytes < BUF_LEN) {
@@ -132,8 +150,17 @@
}
totalBytes += bytes;
}
+#else
+#if defined(__linux__)
+ // On host, <sys/random.h> / GRND_NONBLOCK is not available
+ while (true) {
+ status_t result = getRandomBytes(reinterpret_cast<uint8_t*>(&id), sizeof(id));
+ if (result == OK) {
+ break;
+ }
+ }
+#endif // __linux__
#endif // __ANDROID__
-
return (id & SEQUENCE_NUMBER_MASK) | static_cast<int32_t>(mSource);
}
@@ -212,6 +239,10 @@
return (source & test) == test;
}
+bool isStylusToolType(uint32_t toolType) {
+ return toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
@@ -412,14 +443,6 @@
return true;
}
-void PointerCoords::copyFrom(const PointerCoords& other) {
- bits = other.bits;
- uint32_t count = BitSet64::count(bits);
- for (uint32_t i = 0; i < count; i++) {
- values[i] = other.values[i];
- }
-}
-
void PointerCoords::transform(const ui::Transform& transform) {
const vec2 xy = transform.transform(getXYValue());
setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
@@ -530,21 +553,21 @@
&pointerCoords[getPointerCount()]);
}
-int MotionEvent::getSurfaceRotation() const {
+std::optional<ui::Rotation> MotionEvent::getSurfaceRotation() const {
// The surface rotation is the rotation from the window's coordinate space to that of the
// display. Since the event's transform takes display space coordinates to window space, the
// returned surface rotation is the inverse of the rotation for the surface.
switch (mTransform.getOrientation()) {
case ui::Transform::ROT_0:
- return DISPLAY_ORIENTATION_0;
+ return ui::ROTATION_0;
case ui::Transform::ROT_90:
- return DISPLAY_ORIENTATION_270;
+ return ui::ROTATION_270;
case ui::Transform::ROT_180:
- return DISPLAY_ORIENTATION_180;
+ return ui::ROTATION_180;
case ui::Transform::ROT_270:
- return DISPLAY_ORIENTATION_90;
+ return ui::ROTATION_90;
default:
- return -1;
+ return std::nullopt;
}
}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 3fe03c7..4751a7d 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -182,6 +182,7 @@
mSources(other.mSources),
mKeyboardType(other.mKeyboardType),
mKeyCharacterMap(other.mKeyCharacterMap),
+ mSupportsUsi(other.mSupportsUsi),
mHasVibrator(other.mHasVibrator),
mHasBattery(other.mHasBattery),
mHasButtonUnderPad(other.mHasButtonUnderPad),
@@ -210,6 +211,7 @@
mHasBattery = false;
mHasButtonUnderPad = false;
mHasSensor = false;
+ mSupportsUsi = false;
mMotionRanges.clear();
mSensors.clear();
mLights.clear();
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 163a2fe..b78fae3 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -391,7 +391,9 @@
DEFINE_AXIS(GENERIC_13), \
DEFINE_AXIS(GENERIC_14), \
DEFINE_AXIS(GENERIC_15), \
- DEFINE_AXIS(GENERIC_16)
+ DEFINE_AXIS(GENERIC_16), \
+ DEFINE_AXIS(GESTURE_X_OFFSET), \
+ DEFINE_AXIS(GESTURE_Y_OFFSET)
// NOTE: If you add new LEDs here, you must also add them to Input.h
#define LEDS_SEQUENCE \
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 422e6e0..fa5c41f 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -43,7 +43,6 @@
// Enables debug output for mapping.
#define DEBUG_MAPPING 0
-
namespace android {
static const char* WHITESPACE = " \t\r";
@@ -93,6 +92,7 @@
: mType(other.mType),
mLoadFileName(other.mLoadFileName),
mLayoutOverlayApplied(other.mLayoutOverlayApplied),
+ mKeyRemapping(other.mKeyRemapping),
mKeysByScanCode(other.mKeysByScanCode),
mKeysByUsageCode(other.mKeysByUsageCode) {
for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -114,7 +114,7 @@
if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
return false;
}
- if (mKeys.size() != other.mKeys.size() ||
+ if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() ||
mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
return false;
@@ -131,22 +131,9 @@
}
}
- for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
- if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
- return false;
- }
- if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
- return false;
- }
- }
-
- for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
- if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
- return false;
- }
- if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
- return false;
- }
+ if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode ||
+ mKeysByUsageCode != other.mKeysByUsageCode) {
+ return false;
}
return true;
@@ -258,14 +245,12 @@
}
}
- for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
- mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
- overlay.mKeysByScanCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByScanCode) {
+ mKeysByScanCode.insert_or_assign(it.first, it.second);
}
- for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
- mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
- overlay.mKeysByUsageCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByUsageCode) {
+ mKeysByUsageCode.insert_or_assign(it.first, it.second);
}
mLayoutOverlayApplied = true;
}
@@ -400,11 +385,26 @@
return true;
}
+void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ if (fromKeyCode == toKeyCode) {
+ mKeyRemapping.erase(fromKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
+#endif
+ return;
+ }
+ mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
+ toKeyCode);
+#endif
+}
+
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- *outKeyCode = mKeysByUsageCode.valueAt(index);
+ const auto it = mKeysByUsageCode.find(usageCode);
+ if (it != mKeysByUsageCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -413,9 +413,9 @@
}
}
if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- *outKeyCode = mKeysByScanCode.valueAt(index);
+ const auto it = mKeysByScanCode.find(scanCode);
+ if (it != mKeysByScanCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -431,45 +431,59 @@
return NAME_NOT_FOUND;
}
-void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState,
- int32_t *outKeyCode, int32_t *outMetaState) const {
- *outKeyCode = keyCode;
- *outMetaState = metaState;
+int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const {
+ int32_t toKeyCode = fromKeyCode;
- const Behavior* behavior = getKeyBehavior(keyCode, metaState);
+ const auto it = mKeyRemapping.find(fromKeyCode);
+ if (it != mKeyRemapping.end()) {
+ toKeyCode = it->second;
+ }
+#if DEBUG_MAPPING
+ ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode);
+#endif
+ return toKeyCode;
+}
+
+std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
+ int32_t fromMetaState) const {
+ int32_t toKeyCode = fromKeyCode;
+ int32_t toMetaState = fromMetaState;
+
+ const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState);
if (behavior != nullptr) {
if (behavior->replacementKeyCode) {
- *outKeyCode = behavior->replacementKeyCode;
- int32_t newMetaState = metaState & ~behavior->metaState;
+ toKeyCode = behavior->replacementKeyCode;
+ toMetaState = fromMetaState & ~behavior->metaState;
// Reset dependent meta states.
if (behavior->metaState & AMETA_ALT_ON) {
- newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
+ toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- newMetaState &= ~AMETA_ALT_ON;
+ toMetaState &= ~AMETA_ALT_ON;
}
if (behavior->metaState & AMETA_CTRL_ON) {
- newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
+ toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
}
if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- newMetaState &= ~AMETA_CTRL_ON;
+ toMetaState &= ~AMETA_CTRL_ON;
}
if (behavior->metaState & AMETA_SHIFT_ON) {
- newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
+ toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
- newMetaState &= ~AMETA_SHIFT_ON;
+ toMetaState &= ~AMETA_SHIFT_ON;
}
// ... and put universal bits back if needed
- *outMetaState = normalizeMetaState(newMetaState);
+ toMetaState = normalizeMetaState(toMetaState);
}
}
#if DEBUG_MAPPING
- ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ "
- "replacement keyCode=%d, replacement metaState=0x%08x.",
- keyCode, metaState, *outKeyCode, *outMetaState);
+ ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ "
+ "replacement keyCode=%d, replacement metaState=0x%08x.",
+ fromKeyCode, fromMetaState, toKeyCode, toMetaState);
#endif
+ return std::make_pair(toKeyCode, toMetaState);
}
bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
@@ -720,6 +734,18 @@
return nullptr;
}
}
+ size_t numKeyRemapping = parcel->readInt32();
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < numKeyRemapping; i++) {
+ int32_t key = parcel->readInt32();
+ int32_t value = parcel->readInt32();
+ map->mKeyRemapping.insert_or_assign(key, value);
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ }
size_t numKeysByScanCode = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -727,7 +753,7 @@
for (size_t i = 0; i < numKeysByScanCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByScanCode.add(key, value);
+ map->mKeysByScanCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -739,7 +765,7 @@
for (size_t i = 0; i < numKeysByUsageCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByUsageCode.add(key, value);
+ map->mKeysByUsageCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -773,17 +799,23 @@
}
parcel->writeInt32(0);
}
+ size_t numKeyRemapping = mKeyRemapping.size();
+ parcel->writeInt32(numKeyRemapping);
+ for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) {
+ parcel->writeInt32(fromAndroidKeyCode);
+ parcel->writeInt32(toAndroidKeyCode);
+ }
size_t numKeysByScanCode = mKeysByScanCode.size();
parcel->writeInt32(numKeysByScanCode);
- for (size_t i = 0; i < numKeysByScanCode; i++) {
- parcel->writeInt32(mKeysByScanCode.keyAt(i));
- parcel->writeInt32(mKeysByScanCode.valueAt(i));
+ for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) {
+ parcel->writeInt32(fromScanCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
size_t numKeysByUsageCode = mKeysByUsageCode.size();
parcel->writeInt32(numKeysByUsageCode);
- for (size_t i = 0; i < numKeysByUsageCode; i++) {
- parcel->writeInt32(mKeysByUsageCode.keyAt(i));
- parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+ for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) {
+ parcel->writeInt32(fromUsageCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
}
#endif // __linux__
@@ -950,9 +982,9 @@
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
- KeyedVector<int32_t, int32_t>& map =
- mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+ const auto it = map.find(code);
+ if (it != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -971,7 +1003,7 @@
ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
mapUsage ? "usage" : "scan code", code, keyCode);
#endif
- map.add(code, keyCode);
+ map.insert_or_assign(code, keyCode);
return NO_ERROR;
}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 16ffa10..ed9ac9f 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -116,25 +116,24 @@
Tokenizer* rawTokenizer;
status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
- std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
if (status) {
- ALOGE("Error %d opening property file %s.", status, filename);
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(outMap.get(), tokenizer.get());
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (status) {
- return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
- }
+ return android::base::Error(-status) << "Could not open file: " << filename;
}
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
+ Parser parser(outMap.get(), tokenizer.get());
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(),
+ tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
+ }
+
return std::move(outMap);
}
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index c62e098..c9393f4 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -40,17 +40,20 @@
const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }
-void TouchVideoFrame::rotate(int32_t orientation) {
+void TouchVideoFrame::rotate(ui::Rotation orientation) {
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
rotateQuarterTurn(false /*clockwise*/);
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
rotate180();
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
rotateQuarterTurn(true /*clockwise*/);
break;
+ case ui::ROTATION_0:
+ // No need to rotate if there's no rotation.
+ break;
}
}
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index e2bfb50..5c008b1 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -37,6 +37,10 @@
reset();
}
+VelocityControlParameters& VelocityControl::getParameters() {
+ return mParameters;
+}
+
void VelocityControl::setParameters(const VelocityControlParameters& parameters) {
mParameters = parameters;
reset();
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 4a4f734..19b4684 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -150,6 +150,10 @@
VelocityTracker::~VelocityTracker() {
}
+bool VelocityTracker::isAxisSupported(int32_t axis) {
+ return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end();
+}
+
void VelocityTracker::configureStrategy(int32_t axis) {
const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 5ce10a4..dab843b 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -35,6 +35,14 @@
const int INVALID_INPUT_EVENT_ID = 0;
/**
+ * Every input device has an id. This constant value is used when a valid input device id is not
+ * available.
+ * The virtual keyboard uses -1 as the input device id. Therefore, we use -2 as the value for
+ * an invalid input device.
+ */
+ const int INVALID_INPUT_DEVICE_ID = -2;
+
+ /**
* The input event was injected from accessibility. Used in policyFlags for input event
* injection.
*/
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index c53811a..5aae37d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -10,6 +10,7 @@
cc_test {
name: "libinput_tests",
+ host_supported: true,
srcs: [
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
@@ -24,6 +25,7 @@
static_libs: [
"libgui_window_info_static",
"libinput",
+ "libui-types",
],
cflags: [
"-Wall",
@@ -35,11 +37,13 @@
"libbinder",
"libcutils",
"liblog",
- "libui",
"libutils",
"libvintf",
],
data: ["data/*"],
+ test_options: {
+ unit_test: true,
+ },
test_suites: ["device-tests"],
}
@@ -60,7 +64,6 @@
"libcutils",
"libutils",
"libbinder",
- "libui",
"libbase",
],
}
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index e872fa4..2344463 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -65,6 +65,9 @@
}
void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device.";
+#endif
loadKeyLayout("Generic");
loadKeyCharacterMap("Generic");
}
@@ -131,6 +134,9 @@
}
TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "Can't check kernel configs on host";
+#endif
std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl";
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
@@ -139,6 +145,9 @@
}
TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "Can't check kernel configs on host";
+#endif
std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl";
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath;
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 654b236..081a995 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -73,38 +73,38 @@
TEST(TouchVideoFrame, Rotate90_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2_4times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameOriginal);
}
@@ -113,43 +113,43 @@
TEST(TouchVideoFrame, Rotate180_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_3x2_2times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameOriginal);
}
TEST(TouchVideoFrame, Rotate180_3x3) {
TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
@@ -158,38 +158,38 @@
TEST(TouchVideoFrame, Rotate270_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2_4times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameOriginal);
}
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index e48012b..54feea2 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -65,8 +65,15 @@
EXPECT_NEAR(actual, target, tolerance);
}
-static void checkVelocity(float Vactual, float Vtarget) {
- EXPECT_NEAR_BY_FRACTION(Vactual, Vtarget, VELOCITY_TOLERANCE);
+static void checkVelocity(std::optional<float> Vactual, std::optional<float> Vtarget) {
+ if (Vactual != std::nullopt) {
+ if (Vtarget == std::nullopt) {
+ FAIL() << "Expected no velocity, but found " << *Vactual;
+ }
+ EXPECT_NEAR_BY_FRACTION(*Vactual, *Vtarget, VELOCITY_TOLERANCE);
+ } else if (Vtarget != std::nullopt) {
+ FAIL() << "Expected velocity, but found no velocity";
+ }
}
static void checkCoefficient(float actual, float target) {
@@ -262,26 +269,16 @@
static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
const std::vector<PlanarMotionEventEntry>& motions,
- int32_t axis, float targetVelocity,
+ int32_t axis, std::optional<float> targetVelocity,
uint32_t pointerId = DEFAULT_POINTER_ID) {
- checkVelocity(computePlanarVelocity(strategy, motions, axis, pointerId).value_or(0),
- targetVelocity);
+ checkVelocity(computePlanarVelocity(strategy, motions, axis, pointerId), targetVelocity);
}
static void computeAndCheckAxisScrollVelocity(
const VelocityTracker::Strategy strategy,
const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
std::optional<float> targetVelocity) {
- std::optional<float> velocity = computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL);
- if (velocity && !targetVelocity) {
- FAIL() << "Expected no velocity, but found " << *velocity;
- }
- if (!velocity && targetVelocity) {
- FAIL() << "Expected velocity, but found no velocity";
- }
- if (velocity) {
- checkVelocity(*velocity, *targetVelocity);
- }
+ checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
}
static void computeAndCheckQuadraticEstimate(const std::vector<PlanarMotionEventEntry>& motions,
@@ -302,6 +299,26 @@
}
/*
+ *================== VelocityTracker tests that do not require test motion data ====================
+ */
+TEST(SimpleVelocityTrackerTest, TestSupportedAxis) {
+ // Note that we are testing up to the max possible axis value, plus 3 more values. We are going
+ // beyond the max value to add a bit more protection. "3" is chosen arbitrarily to cover a few
+ // more values beyond the max.
+ for (int32_t axis = 0; axis <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE + 3; axis++) {
+ switch (axis) {
+ case AMOTION_EVENT_AXIS_X:
+ case AMOTION_EVENT_AXIS_Y:
+ case AMOTION_EVENT_AXIS_SCROLL:
+ EXPECT_TRUE(VelocityTracker::isAxisSupported(axis)) << axis << " is supported";
+ break;
+ default:
+ EXPECT_FALSE(VelocityTracker::isAxisSupported(axis)) << axis << " is NOT supported";
+ }
+ }
+}
+
+/*
* ================== VelocityTracker tests generated manually =====================================
*/
TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) {
@@ -1013,7 +1030,7 @@
/**
* ================== Multiple pointers ============================================================
*
- * Three fingers quickly tap the screen. Since this is a tap, the velocities should be zero.
+ * Three fingers quickly tap the screen. Since this is a tap, the velocities should be empty.
* If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be
* part of the fitted data), this can cause large velocity values to be reported instead.
*/
@@ -1027,12 +1044,14 @@
{ 272700us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} },
};
- // Velocity should actually be zero, but we expect 0.016 here instead.
- // This is close enough to zero, and is likely caused by division by a very small number.
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, 0);
- computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+ std::nullopt);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+ std::nullopt);
}
/**
@@ -1055,7 +1074,7 @@
/**
* The last movement of a single pointer is ACTION_UP. If there's a long delay between the last
- * ACTION_MOVE and the final ACTION_UP, velocity should be reported as zero because the pointer
+ * ACTION_MOVE and the final ACTION_UP, velocity should be reported as empty because the pointer
* should be assumed to have stopped.
*/
TEST_F(VelocityTrackerTest, LongDelayBeforeActionUp) {
@@ -1065,14 +1084,16 @@
{20ms, {{30, 0}}},
{3000ms, {{30, 0}}}, // ACTION_UP
};
- computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt);
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt);
}
/**
* The last movement of a pointer is always ACTION_POINTER_UP or ACTION_UP. If there's a long delay
* before ACTION_POINTER_UP event, the movement should be assumed to have stopped.
- * The final velocity should be reported as zero for all pointers.
+ * The final velocity should be reported as empty for all pointers.
*/
TEST_F(VelocityTrackerTest, LongDelayBeforeActionPointerUp) {
std::vector<PlanarMotionEventEntry> motions = {
@@ -1083,13 +1104,17 @@
{40ms, {{30, 0}, {400, 0}}},
{3000ms, {{30, 0}}}, // ACTION_POINTER_UP
};
- computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0,
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt,
/*pointerId*/ 0);
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0,
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt,
/*pointerId*/ 0);
- computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0,
+ computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt,
/*pointerId*/ 1);
- computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0,
+ computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+ std::nullopt,
/*pointerId*/ 1);
}
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
new file mode 100644
index 0000000..54af7c9
--- /dev/null
+++ b/libs/jpegrecoverymap/Android.bp
@@ -0,0 +1,70 @@
+// 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.
+
+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_static {
+ name: "libjpegrecoverymap",
+ host_supported: true,
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ srcs: [
+ "recoverymap.cpp",
+ "recoverymapmath.cpp",
+ "recoverymaputils.cpp",
+ ],
+
+ shared_libs: [
+ "libimage_io",
+ "libjpeg",
+ "libutils",
+ ],
+}
+
+cc_library_static {
+ name: "libjpegencoder",
+
+ shared_libs: [
+ "libjpeg",
+ ],
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "jpegencoder.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libjpegdecoder",
+
+ shared_libs: [
+ "libjpeg",
+ ],
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "jpegdecoder.cpp",
+ ],
+}
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS
new file mode 100644
index 0000000..133af5b
--- /dev/null
+++ b/libs/jpegrecoverymap/OWNERS
@@ -0,0 +1,4 @@
+arifdikici@google.com
+deakin@google.com
+dichenzhang@google.com
+kyslov@google.com
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
new file mode 100644
index 0000000..5c9c8b6
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -0,0 +1,99 @@
+
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H
+#define ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H
+
+// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
+#include <cstdio>
+extern "C" {
+#include <jerror.h>
+#include <jpeglib.h>
+}
+#include <utils/Errors.h>
+#include <vector>
+namespace android::recoverymap {
+/*
+ * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
+ * This class is not thread-safe.
+ */
+class JpegDecoder {
+public:
+ JpegDecoder();
+ ~JpegDecoder();
+ /*
+ * Decompresses JPEG image to raw image (YUV420planer or grey-scale) format. After calling
+ * this method, call getDecompressedImage() to get the image.
+ * Returns false if decompressing the image fails.
+ */
+ bool decompressImage(const void* image, int length);
+ /*
+ * Returns the decompressed raw image buffer pointer. This method must be called only after
+ * calling decompressImage().
+ */
+ void* getDecompressedImagePtr();
+ /*
+ * Returns the decompressed raw image buffer size. This method must be called only after
+ * calling decompressImage().
+ */
+ size_t getDecompressedImageSize();
+ /*
+ * Returns the image width in pixels. This method must be called only after calling
+ * decompressImage().
+ */
+ size_t getDecompressedImageWidth();
+ /*
+ * Returns the image width in pixels. This method must be called only after calling
+ * decompressImage().
+ */
+ size_t getDecompressedImageHeight();
+ /*
+ * Returns the XMP data from the image.
+ */
+ void* getXMPPtr();
+ /*
+ * Returns the decompressed XMP buffer size. This method must be called only after
+ * calling decompressImage().
+ */
+ size_t getXMPSize();
+
+ bool getCompressedImageParameters(const void* image, int length,
+ size_t* pWidth, size_t* pHeight,
+ std::vector<uint8_t>* &iccData,
+ std::vector<uint8_t>* &exifData);
+
+private:
+ bool decode(const void* image, int length);
+ // Returns false if errors occur.
+ bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
+ bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
+ // Process 16 lines of Y and 16 lines of U/V each time.
+ // We must pass at least 16 scanlines according to libjpeg documentation.
+ static const int kCompressBatchSize = 16;
+ // The buffer that holds the decompressed result.
+ std::vector<JOCTET> mResultBuffer;
+ // The buffer that holds XMP Data.
+ std::vector<JOCTET> mXMPBuffer;
+
+ // Resolution of the decompressed image.
+ size_t mWidth;
+ size_t mHeight;
+};
+} /* namespace android */
+
+#endif // ANDROID_JPEGRECOVERYMAP_JPEGDECODER_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h
new file mode 100644
index 0000000..61aeb8a
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegencoder.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H
+#define ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H
+
+// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
+#include <cstdio>
+
+extern "C" {
+#include <jerror.h>
+#include <jpeglib.h>
+}
+
+#include <utils/Errors.h>
+#include <vector>
+
+namespace android::recoverymap {
+
+/*
+ * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
+ * This class is not thread-safe.
+ */
+class JpegEncoder {
+public:
+ JpegEncoder();
+ ~JpegEncoder();
+
+ /*
+ * Compresses YUV420Planer image to JPEG format. After calling this method, call
+ * getCompressedImage() to get the image. |quality| is the jpeg image quality parameter to use.
+ * It ranges from 1 (poorest quality) to 100 (highest quality). |iccBuffer| is the buffer of
+ * ICC segment which will be added to the compressed image.
+ * Returns false if errors occur during compression.
+ */
+ bool compressImage(const void* image, int width, int height, int quality,
+ const void* iccBuffer, unsigned int iccSize, bool isSingleChannel = false);
+
+ /*
+ * Returns the compressed JPEG buffer pointer. This method must be called only after calling
+ * compressImage().
+ */
+ void* getCompressedImagePtr();
+
+ /*
+ * Returns the compressed JPEG buffer size. This method must be called only after calling
+ * compressImage().
+ */
+ size_t getCompressedImageSize();
+
+private:
+ // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
+ // passed into jpeg library.
+ static void initDestination(j_compress_ptr cinfo);
+ static boolean emptyOutputBuffer(j_compress_ptr cinfo);
+ static void terminateDestination(j_compress_ptr cinfo);
+ static void outputErrorMessage(j_common_ptr cinfo);
+
+ // Returns false if errors occur.
+ bool encode(const void* inYuv, int width, int height, int jpegQuality,
+ const void* iccBuffer, unsigned int iccSize, bool isSingleChannel);
+ void setJpegDestination(jpeg_compress_struct* cinfo);
+ void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
+ bool isSingleChannel);
+ // Returns false if errors occur.
+ bool compress(jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel);
+ bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv);
+ bool compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image);
+
+ // The block size for encoded jpeg image buffer.
+ static const int kBlockSize = 16384;
+ // Process 16 lines of Y and 16 lines of U/V each time.
+ // We must pass at least 16 scanlines according to libjpeg documentation.
+ static const int kCompressBatchSize = 16;
+
+ // The buffer that holds the compressed result.
+ std::vector<JOCTET> mResultBuffer;
+};
+
+} /* namespace android */
+
+#endif // ANDROID_JPEGRECOVERYMAP_JPEGENCODER_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
new file mode 100644
index 0000000..6995762
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -0,0 +1,50 @@
+/*
+ * 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 <utils/Errors.h>
+
+namespace android::recoverymap {
+
+enum {
+ // status_t map for errors in the media framework
+ // OK or NO_ERROR or 0 represents no error.
+
+ // See system/core/include/utils/Errors.h
+ // System standard errors from -1 through (possibly) -133
+ //
+ // Errors with special meanings and side effects.
+ // INVALID_OPERATION: Operation attempted in an illegal state (will try to signal to app).
+ // DEAD_OBJECT: Signal from CodecBase to MediaCodec that MediaServer has died.
+ // NAME_NOT_FOUND: Signal from CodecBase to MediaCodec that the component was not found.
+
+ // JPEGR errors
+ JPEGR_IO_ERROR_BASE = -10000,
+ ERROR_JPEGR_INVALID_INPUT_TYPE = JPEGR_IO_ERROR_BASE,
+ ERROR_JPEGR_INVALID_OUTPUT_TYPE = JPEGR_IO_ERROR_BASE - 1,
+ ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2,
+ ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3,
+ ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
+ ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
+
+ JPEGR_RUNTIME_ERROR_BASE = -20000,
+ ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
+ ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
+ ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
+ ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
+ ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
+};
+
+} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
new file mode 100644
index 0000000..74f9776
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -0,0 +1,400 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H
+#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H
+
+#include "jpegrerrorcode.h"
+
+namespace android::recoverymap {
+
+typedef enum {
+ JPEGR_COLORGAMUT_UNSPECIFIED,
+ JPEGR_COLORGAMUT_BT709,
+ JPEGR_COLORGAMUT_P3,
+ JPEGR_COLORGAMUT_BT2100,
+} jpegr_color_gamut;
+
+// Transfer functions as defined for XMP metadata
+typedef enum {
+ JPEGR_TF_HLG = 0,
+ JPEGR_TF_PQ = 1,
+} jpegr_transfer_function;
+
+struct jpegr_info_struct {
+ size_t width;
+ size_t height;
+ std::vector<uint8_t>* iccData;
+ std::vector<uint8_t>* exifData;
+};
+
+/*
+ * Holds information for uncompressed image or recovery map.
+ */
+struct jpegr_uncompressed_struct {
+ // Pointer to the data location.
+ void* data;
+ // Width of the recovery map or image in pixels.
+ int width;
+ // Height of the recovery map or image in pixels.
+ int height;
+ // Color gamut.
+ jpegr_color_gamut colorGamut;
+};
+
+/*
+ * Holds information for compressed image or recovery map.
+ */
+struct jpegr_compressed_struct {
+ // Pointer to the data location.
+ void* data;
+ // Used data length in bytes.
+ int length;
+ // Maximum available data length in bytes.
+ int maxLength;
+ // Color gamut.
+ jpegr_color_gamut colorGamut;
+};
+
+/*
+ * Holds information for EXIF metadata.
+ */
+struct jpegr_exif_struct {
+ // Pointer to the data location.
+ void* data;
+ // Data length;
+ int length;
+};
+
+struct chromaticity_coord {
+ float x;
+ float y;
+};
+
+
+struct st2086_metadata {
+ // xy chromaticity coordinate of the red primary of the mastering display
+ chromaticity_coord redPrimary;
+ // xy chromaticity coordinate of the green primary of the mastering display
+ chromaticity_coord greenPrimary;
+ // xy chromaticity coordinate of the blue primary of the mastering display
+ chromaticity_coord bluePrimary;
+ // xy chromaticity coordinate of the white point of the mastering display
+ chromaticity_coord whitePoint;
+ // Maximum luminance in nits of the mastering display
+ uint32_t maxLuminance;
+ // Minimum luminance in nits of the mastering display
+ float minLuminance;
+};
+
+struct hdr10_metadata {
+ // Mastering display color volume
+ st2086_metadata st2086Metadata;
+ // Max frame average light level in nits
+ float maxFALL;
+ // Max content light level in nits
+ float maxCLL;
+};
+
+struct jpegr_metadata {
+ // JPEG/R version
+ uint32_t version;
+ // Range scaling factor for the map
+ float rangeScalingFactor;
+ // The transfer function for decoding the HDR representation of the image
+ jpegr_transfer_function transferFunction;
+ // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ
+ hdr10_metadata hdr10Metadata;
+};
+
+typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
+typedef struct jpegr_compressed_struct* jr_compressed_ptr;
+typedef struct jpegr_exif_struct* jr_exif_ptr;
+typedef struct jpegr_metadata* jr_metadata_ptr;
+typedef struct jpegr_info_struct* jr_info_ptr;
+
+class RecoveryMap {
+public:
+ /*
+ * Encode API-0
+ * Compress JPEGR image from 10-bit HDR YUV.
+ *
+ * Tonemap the HDR input to a SDR image, generate recovery map from the HDR and SDR images,
+ * compress SDR YUV to 8-bit JPEG and append the recovery map to the end of the compressed
+ * JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif);
+
+ /*
+ * Encode API-1
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
+ * the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same
+ * resolution.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif);
+
+ /*
+ * Encode API-2
+ * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
+ *
+ * This method requires HAL Hardware JPEG encoder.
+ *
+ * Generate recovery map from the HDR and SDR inputs, append the recovery map to the end of the
+ * compressed JPEG. HDR and SDR inputs must be the same resolution and color space.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * Note: the SDR image must be the decoded version of the JPEG
+ * input
+ * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest);
+
+ /*
+ * Encode API-3
+ * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
+ *
+ * This method requires HAL Hardware JPEG encoder.
+ *
+ * Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input
+ * and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR
+ * and SDR inputs must be the same resolution.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest);
+
+ /*
+ * Decode API
+ * Decompress JPEGR image.
+ *
+ * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param dest destination of the uncompressed JPEGR image
+ * @param exif destination of the decoded EXIF metadata.
+ * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
+ * the primary image which is SDR. Setting of request_sdr and input source
+ * (HDR or SDR) can be found in the table below:
+ * | input source | request_sdr | output of decoding |
+ * | HDR | true | SDR |
+ * | HDR | false | HDR |
+ * | SDR | true | SDR |
+ * | SDR | false | SDR |
+ * @return NO_ERROR if decoding succeeds, error code if error occurs.
+ */
+ status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
+ jr_uncompressed_ptr dest,
+ jr_exif_ptr exif = nullptr,
+ bool request_sdr = false);
+
+ /*
+ * Gets Info from JPEGR file without decoding it.
+ *
+ * The output is filled jpegr_info structure
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param jpegr_info pointer to output JPEGR info
+ * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
+ */
+ status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+ jr_info_ptr jpegr_info);
+private:
+ /*
+ * This method is called in the decoding pipeline. It will decode the recovery map.
+ *
+ * @param compressed_recovery_map compressed recovery map
+ * @param dest decoded recover map
+ * @return NO_ERROR if decoding succeeds, error code if error occurs.
+ */
+ status_t decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map,
+ jr_uncompressed_ptr dest);
+
+ /*
+ * This method is called in the encoding pipeline. It will encode the recovery map.
+ *
+ * @param uncompressed_recovery_map uncompressed recovery map
+ * @param dest encoded recover map
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_compressed_ptr dest);
+
+ /*
+ * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
+ * 10-bit yuv images as input, and calculate the uncompressed recovery map. The input images
+ * must be the same resolution.
+ *
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param dest recovery map; caller responsible for memory of data
+ * @param metadata metadata provides the transfer function for the HDR
+ * image; range_scaling_factor and hdr10 FALL and CLL will
+ * be updated.
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_p010_image,
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest);
+
+ /*
+ * This method is called in the decoding pipeline. It will take the uncompressed (decoded)
+ * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as
+ * input, and calculate the 10-bit recovered image. The recovered output image is the same
+ * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata,
+ * and is in RGBA1010102 data format.
+ *
+ * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param uncompressed_recovery_map uncompressed recovery map
+ * @param metadata JPEG/R metadata extracted from XMP.
+ * @param dest reconstructed HDR image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest);
+
+ /*
+ * This methoud is called to separate primary image and recovery map image from JPEGR
+ *
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param primary_image destination of primary image
+ * @param recovery_map destination of compressed recovery map
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map);
+ /*
+ * This method is called in the decoding pipeline. It will read XMP metadata to find the start
+ * position of the compressed recovery map, and will extract the compressed recovery map.
+ *
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param dest destination of compressed recovery map
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr dest);
+
+ /*
+ * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
+ * and the compressed recovery map as input, and update the XMP metadata with the end of JPEG
+ * marker, and append the compressed gian map after the JPEG.
+ *
+ * @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param compress_recovery_map compressed recover map
+ * @param metadata JPEG/R metadata to encode in XMP of the jpeg
+ * @param dest compressed JPEGR image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
+ jr_compressed_ptr compressed_recovery_map,
+ jr_metadata_ptr metadata,
+ jr_compressed_ptr dest);
+
+ /*
+ * This method generates XMP metadata.
+ *
+ * below is an example of the XMP metadata that this function generates where
+ * secondary_image_length = 1000
+ * range_scaling_factor = 1.25
+ *
+ * <x:xmpmeta
+ * xmlns:x="adobe:ns:meta/"
+ * x:xmptk="Adobe XMP Core 5.1.2">
+ * <rdf:RDF
+ * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ * <rdf:Description
+ * xmlns:GContainer="http://ns.google.com/photos/1.0/container/">
+ * <GContainer:Version>1</GContainer:Version>
+ * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor>
+ * <GContainer:Directory>
+ * <rdf:Seq>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="Primary"
+ * Item:Mime="image/jpeg"/>
+ * </rdf:li>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="RecoveryMap"
+ * Item:Mime="image/jpeg"
+ * Item:Length="1000"/>
+ * </rdf:li>
+ * </rdf:Seq>
+ * </GContainer:Directory>
+ * </rdf:Description>
+ * </rdf:RDF>
+ * </x:xmpmeta>
+ *
+ * @param secondary_image_length length of secondary image
+ * @param metadata JPEG/R metadata to encode as XMP
+ * @return XMP metadata in type of string
+ */
+ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+
+ /*
+ * This method will tone map a HDR image to an SDR image.
+ *
+ * @param uncompressed_p010_image (input) uncompressed P010 image
+ * @param dest (output) tone mapping result as a YUV_420 image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest);
+};
+
+} // namespace android::recoverymap
+
+#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAP_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
new file mode 100644
index 0000000..0fb64d3
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
+#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
+
+#include <stdint.h>
+
+#include <jpegrecoverymap/recoverymap.h>
+
+namespace android::recoverymap {
+
+////////////////////////////////////////////////////////////////////////////////
+// Framework
+
+const float kSdrWhiteNits = 100.0f;
+const float kHlgMaxNits = 1000.0f;
+const float kPqMaxNits = 10000.0f;
+
+struct Color {
+ union {
+ struct {
+ float r;
+ float g;
+ float b;
+ };
+ struct {
+ float y;
+ float u;
+ float v;
+ };
+ };
+};
+
+typedef Color (*ColorTransformFn)(Color);
+typedef float (*ColorCalculationFn)(Color);
+
+inline Color operator+=(Color& lhs, const Color& rhs) {
+ lhs.r += rhs.r;
+ lhs.g += rhs.g;
+ lhs.b += rhs.b;
+ return lhs;
+}
+inline Color operator-=(Color& lhs, const Color& rhs) {
+ lhs.r -= rhs.r;
+ lhs.g -= rhs.g;
+ lhs.b -= rhs.b;
+ return lhs;
+}
+
+inline Color operator+(const Color& lhs, const Color& rhs) {
+ Color temp = lhs;
+ return temp += rhs;
+}
+inline Color operator-(const Color& lhs, const Color& rhs) {
+ Color temp = lhs;
+ return temp -= rhs;
+}
+
+inline Color operator+=(Color& lhs, const float rhs) {
+ lhs.r += rhs;
+ lhs.g += rhs;
+ lhs.b += rhs;
+ return lhs;
+}
+inline Color operator-=(Color& lhs, const float rhs) {
+ lhs.r -= rhs;
+ lhs.g -= rhs;
+ lhs.b -= rhs;
+ return lhs;
+}
+inline Color operator*=(Color& lhs, const float rhs) {
+ lhs.r *= rhs;
+ lhs.g *= rhs;
+ lhs.b *= rhs;
+ return lhs;
+}
+inline Color operator/=(Color& lhs, const float rhs) {
+ lhs.r /= rhs;
+ lhs.g /= rhs;
+ lhs.b /= rhs;
+ return lhs;
+}
+
+inline Color operator+(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp += rhs;
+}
+inline Color operator-(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp -= rhs;
+}
+inline Color operator*(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp *= rhs;
+}
+inline Color operator/(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp /= rhs;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// sRGB transformations
+// NOTE: sRGB has the same color primaries as BT.709, but different transfer
+// function. For this reason, all sRGB transformations here apply to BT.709,
+// except for those concerning transfer functions.
+
+/*
+ * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float srgbLuminance(Color e);
+
+/*
+ * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98.
+ */
+Color srgbYuvToRgb(Color e_gamma);
+
+/*
+ * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98.
+ */
+Color srgbRgbToYuv(Color e_gamma);
+
+/*
+ * Convert from srgb to linear, according to IEC 61966-2-1.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float srgbInvOetf(float e_gamma);
+Color srgbInvOetf(Color e_gamma);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Display-P3 transformations
+
+/*
+ * Calculated the luminance of a linear RGB P3 pixel, according to SMPTE EG 432-1.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float p3Luminance(Color e);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// BT.2100 transformations - according to ITU-R BT.2100-2
+
+/*
+ * Calculate the luminance of a linear RGB BT.2100 pixel.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float bt2100Luminance(Color e);
+
+/*
+ * Convert from OETF'd BT.2100 RGB to YUV.
+ */
+Color bt2100RgbToYuv(Color e_gamma);
+
+/*
+ * Convert from OETF'd BT.2100 YUV to RGB.
+ */
+Color bt2100YuvToRgb(Color e_gamma);
+
+/*
+ * Convert from scene luminance to HLG.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float hlgOetf(float e);
+Color hlgOetf(Color e);
+
+/*
+ * Convert from HLG to scene luminance.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float hlgInvOetf(float e_gamma);
+Color hlgInvOetf(Color e_gamma);
+
+/*
+ * Convert from scene luminance to PQ.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float pqOetf(float e);
+Color pqOetf(Color e);
+
+/*
+ * Convert from PQ to scene luminance in nits.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float pqInvOetf(float e_gamma);
+Color pqInvOetf(Color e_gamma);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Color space conversions
+
+/*
+ * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1.
+ *
+ * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the
+ * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is
+ * always the inverse of the RGB gamut to XYZ matrix.
+ */
+Color bt709ToP3(Color e);
+Color bt709ToBt2100(Color e);
+Color p3ToBt709(Color e);
+Color p3ToBt2100(Color e);
+Color bt2100ToBt709(Color e);
+Color bt2100ToP3(Color e);
+
+/*
+ * Identity conversion.
+ */
+inline Color identityConversion(Color e) { return e; }
+
+/*
+ * Get the conversion to apply to the HDR image for recovery map generation
+ */
+ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Recovery map calculations
+
+/*
+ * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR
+ * luminances in linear space, and the hdr ratio to encode against.
+ */
+uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio);
+
+/*
+ * Calculates the linear luminance in nits after applying the given recovery
+ * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
+ */
+Color applyRecovery(Color e, float recovery, float hdr_ratio);
+
+/*
+ * Helper for sampling from YUV 420 images.
+ */
+Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
+
+/*
+ * Helper for sampling from P010 images.
+ *
+ * Expect narrow-range image data for P010.
+ */
+Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
+
+/*
+ * Sample the image at the provided location, with a weighting based on nearby
+ * pixels and the map scale factor.
+ */
+Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
+ * Sample the image at the provided location, with a weighting based on nearby
+ * pixels and the map scale factor.
+ *
+ * Expect narrow-range image data for P010.
+ */
+Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
+ * Sample the recovery value for the map from a given x,y coordinate on a scale
+ * that is map scale factor larger than the map size.
+ */
+float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
+ * Convert from Color to RGBA1010102.
+ *
+ * Alpha always set to 1.0.
+ */
+uint32_t colorToRgba1010102(Color e_gamma);
+
+} // namespace android::recoverymap
+
+#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
new file mode 100644
index 0000000..e35f2d7
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+
+#include <stdint.h>
+#include <cstdio>
+
+
+namespace android::recoverymap {
+
+struct jpegr_metadata;
+
+/*
+ * Parses XMP packet and fills metadata with data from XMP
+ *
+ * @param xmp_data pointer to XMP packet
+ * @param xmp_size size of XMP packet
+ * @param metadata place to store HDR metadata values
+ * @return true if metadata is successfully retrieved, false otherwise
+*/
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+
+}
+
+#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
new file mode 100644
index 0000000..0185e55
--- /dev/null
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -0,0 +1,309 @@
+/*
+ * 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 <jpegrecoverymap/jpegdecoder.h>
+
+#include <cutils/log.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <string>
+
+using namespace std;
+
+namespace android::recoverymap {
+
+const uint32_t kExifMarker = JPEG_APP0 + 1;
+const uint32_t kICCMarker = JPEG_APP0 + 2;
+
+struct jpegr_source_mgr : jpeg_source_mgr {
+ jpegr_source_mgr(const uint8_t* ptr, int len);
+ ~jpegr_source_mgr();
+
+ const uint8_t* mBufferPtr;
+ size_t mBufferLength;
+};
+
+struct jpegrerror_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+static void jpegr_init_source(j_decompress_ptr cinfo) {
+ jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
+ src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
+ src->bytes_in_buffer = src->mBufferLength;
+}
+
+static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
+ ALOGE("%s : should not get here", __func__);
+ return FALSE;
+}
+
+static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
+ jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
+
+ if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
+ ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
+ } else {
+ src->next_input_byte += num_bytes;
+ src->bytes_in_buffer -= num_bytes;
+ }
+}
+
+static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
+
+jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len) :
+ mBufferPtr(ptr), mBufferLength(len) {
+ init_source = jpegr_init_source;
+ fill_input_buffer = jpegr_fill_input_buffer;
+ skip_input_data = jpegr_skip_input_data;
+ resync_to_restart = jpeg_resync_to_restart;
+ term_source = jpegr_term_source;
+}
+
+jpegr_source_mgr::~jpegr_source_mgr() {}
+
+static void jpegrerror_exit(j_common_ptr cinfo) {
+ jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
+ longjmp(err->setjmp_buffer, 1);
+}
+
+JpegDecoder::JpegDecoder() {
+}
+
+JpegDecoder::~JpegDecoder() {
+}
+
+bool JpegDecoder::decompressImage(const void* image, int length) {
+ if (image == nullptr || length <= 0) {
+ ALOGE("Image size can not be handled: %d", length);
+ return false;
+ }
+
+ mResultBuffer.clear();
+ mXMPBuffer.clear();
+ if (!decode(image, length)) {
+ return false;
+ }
+
+ return true;
+}
+
+void* JpegDecoder::getDecompressedImagePtr() {
+ return mResultBuffer.data();
+}
+
+size_t JpegDecoder::getDecompressedImageSize() {
+ return mResultBuffer.size();
+}
+
+void* JpegDecoder::getXMPPtr() {
+ return mXMPBuffer.data();
+}
+
+size_t JpegDecoder::getXMPSize() {
+ return mXMPBuffer.size();
+}
+
+
+size_t JpegDecoder::getDecompressedImageWidth() {
+ return mWidth;
+}
+
+size_t JpegDecoder::getDecompressedImageHeight() {
+ return mHeight;
+}
+
+bool JpegDecoder::decode(const void* image, int length) {
+ jpeg_decompress_struct cinfo;
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ jpegrerror_mgr myerr;
+ string nameSpace = "http://ns.adobe.com/xap/1.0/";
+
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
+
+ cinfo.src = &mgr;
+ jpeg_read_header(&cinfo, TRUE);
+
+ // Save XMP Data
+ for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
+ if (marker->marker == kExifMarker) {
+ const unsigned int len = marker->data_length;
+ if (len > nameSpace.size() &&
+ !strncmp(reinterpret_cast<const char*>(marker->data),
+ nameSpace.c_str(), nameSpace.size())) {
+ mXMPBuffer.resize(len+1, 0);
+ memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
+ break;
+ }
+ }
+ }
+
+
+ mWidth = cinfo.image_width;
+ mHeight = cinfo.image_height;
+
+ if (cinfo.jpeg_color_space == JCS_YCbCr) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
+ } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
+ }
+
+ cinfo.raw_data_out = TRUE;
+ cinfo.dct_method = JDCT_IFAST;
+ cinfo.out_color_space = cinfo.jpeg_color_space;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
+ cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
+ return false;
+ }
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ return true;
+}
+
+bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
+ bool isSingleChannel) {
+ if (isSingleChannel) {
+ return decompressSingleChannel(cinfo, dest);
+ }
+ return decompressYUV(cinfo, dest);
+}
+
+bool JpegDecoder::getCompressedImageParameters(const void* image, int length,
+ size_t *pWidth, size_t *pHeight,
+ std::vector<uint8_t> *&iccData , std::vector<uint8_t> *&exifData) {
+ jpeg_decompress_struct cinfo;
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ jpegrerror_mgr myerr;
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF);
+
+ cinfo.src = &mgr;
+ if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+
+ *pWidth = cinfo.image_width;
+ *pHeight = cinfo.image_height;
+
+ //TODO: Parse iccProfile and exifData
+ (void)iccData;
+ (void)exifData;
+
+
+ jpeg_destroy_decompress(&cinfo);
+ return true;
+}
+
+
+bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3] {y, cb, cr};
+
+ size_t y_plane_size = cinfo->image_width * cinfo->image_height;
+ size_t uv_plane_size = y_plane_size / 4;
+ uint8_t* y_plane = const_cast<uint8_t*>(dest);
+ uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
+ uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->output_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->output_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * (cinfo->image_width / 2);
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ } else {
+ cb[i] = cr[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool JpegDecoder::decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPARRAY planes[1] {y};
+
+ uint8_t* y_plane = const_cast<uint8_t*>(dest);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->output_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->output_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_read_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize / 2) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/jpegrecoverymap/jpegencoder.cpp b/libs/jpegrecoverymap/jpegencoder.cpp
new file mode 100644
index 0000000..1997bf9
--- /dev/null
+++ b/libs/jpegrecoverymap/jpegencoder.cpp
@@ -0,0 +1,239 @@
+/*
+ * 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 <jpegrecoverymap/jpegencoder.h>
+
+#include <cutils/log.h>
+
+#include <errno.h>
+
+namespace android::recoverymap {
+
+// The destination manager that can access |mResultBuffer| in JpegEncoder.
+struct destination_mgr {
+public:
+ struct jpeg_destination_mgr mgr;
+ JpegEncoder* encoder;
+};
+
+JpegEncoder::JpegEncoder() {
+}
+
+JpegEncoder::~JpegEncoder() {
+}
+
+bool JpegEncoder::compressImage(const void* image, int width, int height, int quality,
+ const void* iccBuffer, unsigned int iccSize,
+ bool isSingleChannel) {
+ if (width % 8 != 0 || height % 2 != 0) {
+ ALOGE("Image size can not be handled: %dx%d", width, height);
+ return false;
+ }
+
+ mResultBuffer.clear();
+ if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) {
+ return false;
+ }
+ ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes",
+ (width * height * 12) / 8, width, height, mResultBuffer.size());
+ return true;
+}
+
+void* JpegEncoder::getCompressedImagePtr() {
+ return mResultBuffer.data();
+}
+
+size_t JpegEncoder::getCompressedImageSize() {
+ return mResultBuffer.size();
+}
+
+void JpegEncoder::initDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ buffer.resize(kBlockSize);
+ dest->mgr.next_output_byte = &buffer[0];
+ dest->mgr.free_in_buffer = buffer.size();
+}
+
+boolean JpegEncoder::emptyOutputBuffer(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ size_t oldsize = buffer.size();
+ buffer.resize(oldsize + kBlockSize);
+ dest->mgr.next_output_byte = &buffer[oldsize];
+ dest->mgr.free_in_buffer = kBlockSize;
+ return true;
+}
+
+void JpegEncoder::terminateDestination(j_compress_ptr cinfo) {
+ destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
+ std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
+ buffer.resize(buffer.size() - dest->mgr.free_in_buffer);
+}
+
+void JpegEncoder::outputErrorMessage(j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message) (cinfo, buffer);
+ ALOGE("%s\n", buffer);
+}
+
+bool JpegEncoder::encode(const void* image, int width, int height, int jpegQuality,
+ const void* iccBuffer, unsigned int iccSize, bool isSingleChannel) {
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ // Override output_message() to print error log with ALOGE().
+ cinfo.err->output_message = &outputErrorMessage;
+ jpeg_create_compress(&cinfo);
+ setJpegDestination(&cinfo);
+
+ setJpegCompressStruct(width, height, jpegQuality, &cinfo, isSingleChannel);
+ jpeg_start_compress(&cinfo, TRUE);
+
+ if (iccBuffer != nullptr && iccSize > 0) {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
+ }
+
+ if (!compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel)) {
+ return false;
+ }
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ return true;
+}
+
+void JpegEncoder::setJpegDestination(jpeg_compress_struct* cinfo) {
+ destination_mgr* dest = static_cast<struct destination_mgr *>((*cinfo->mem->alloc_small) (
+ (j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(destination_mgr)));
+ dest->encoder = this;
+ dest->mgr.init_destination = &initDestination;
+ dest->mgr.empty_output_buffer = &emptyOutputBuffer;
+ dest->mgr.term_destination = &terminateDestination;
+ cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest);
+}
+
+void JpegEncoder::setJpegCompressStruct(int width, int height, int quality,
+ jpeg_compress_struct* cinfo, bool isSingleChannel) {
+ cinfo->image_width = width;
+ cinfo->image_height = height;
+ if (isSingleChannel) {
+ cinfo->input_components = 1;
+ cinfo->in_color_space = JCS_GRAYSCALE;
+ } else {
+ cinfo->input_components = 3;
+ cinfo->in_color_space = JCS_YCbCr;
+ }
+ jpeg_set_defaults(cinfo);
+
+ jpeg_set_quality(cinfo, quality, TRUE);
+ jpeg_set_colorspace(cinfo, isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr);
+ cinfo->raw_data_in = TRUE;
+ cinfo->dct_method = JDCT_IFAST;
+
+ if (!isSingleChannel) {
+ // Configure sampling factors. The sampling factor is JPEG subsampling 420 because the
+ // source format is YUV420.
+ cinfo->comp_info[0].h_samp_factor = 2;
+ cinfo->comp_info[0].v_samp_factor = 2;
+ cinfo->comp_info[1].h_samp_factor = 1;
+ cinfo->comp_info[1].v_samp_factor = 1;
+ cinfo->comp_info[2].h_samp_factor = 1;
+ cinfo->comp_info[2].v_samp_factor = 1;
+ }
+}
+
+bool JpegEncoder::compress(
+ jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel) {
+ if (isSingleChannel) {
+ return compressSingleChannel(cinfo, image);
+ }
+ return compressYuv(cinfo, image);
+}
+
+bool JpegEncoder::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPROW cb[kCompressBatchSize / 2];
+ JSAMPROW cr[kCompressBatchSize / 2];
+ JSAMPARRAY planes[3] {y, cb, cr};
+
+ size_t y_plane_size = cinfo->image_width * cinfo->image_height;
+ size_t uv_plane_size = y_plane_size / 4;
+ uint8_t* y_plane = const_cast<uint8_t*>(yuv);
+ uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size);
+ uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ // cb, cr only have half scanlines
+ for (int i = 0; i < kCompressBatchSize / 2; ++i) {
+ size_t scanline = cinfo->next_scanline / 2 + i;
+ if (scanline < cinfo->image_height / 2) {
+ int offset = scanline * (cinfo->image_width / 2);
+ cb[i] = u_plane + offset;
+ cr[i] = v_plane + offset;
+ } else {
+ cb[i] = cr[i] = empty.get();
+ }
+ }
+
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool JpegEncoder::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image) {
+ JSAMPROW y[kCompressBatchSize];
+ JSAMPARRAY planes[1] {y};
+
+ uint8_t* y_plane = const_cast<uint8_t*>(image);
+ std::unique_ptr<uint8_t[]> empty(new uint8_t[cinfo->image_width]);
+ memset(empty.get(), 0, cinfo->image_width);
+
+ while (cinfo->next_scanline < cinfo->image_height) {
+ for (int i = 0; i < kCompressBatchSize; ++i) {
+ size_t scanline = cinfo->next_scanline + i;
+ if (scanline < cinfo->image_height) {
+ y[i] = y_plane + scanline * cinfo->image_width;
+ } else {
+ y[i] = empty.get();
+ }
+ }
+ int processed = jpeg_write_raw_data(cinfo, planes, kCompressBatchSize);
+ if (processed != kCompressBatchSize / 2) {
+ ALOGE("Number of processed lines does not equal input lines.");
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
new file mode 100644
index 0000000..c9ac921
--- /dev/null
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -0,0 +1,750 @@
+/*
+ * 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 <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/jpegencoder.h>
+#include <jpegrecoverymap/jpegdecoder.h>
+#include <jpegrecoverymap/recoverymapmath.h>
+#include <jpegrecoverymap/recoverymaputils.h>
+
+#include <image_io/jpeg/jpeg_marker.h>
+#include <image_io/xml/xml_writer.h>
+#include <image_io/jpeg/jpeg_info.h>
+#include <image_io/jpeg/jpeg_scanner.h>
+#include <image_io/jpeg/jpeg_info_builder.h>
+#include <image_io/base/data_segment_data_source.h>
+#include <utils/Log.h>
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <cmath>
+
+using namespace std;
+using namespace photos_editing_formats::image_io;
+
+namespace android::recoverymap {
+
+#define JPEGR_CHECK(x) \
+ { \
+ status_t status = (x); \
+ if ((status) != NO_ERROR) { \
+ return status; \
+ } \
+ }
+
+// The current JPEGR version that we encode to
+static const uint32_t kJpegrVersion = 1;
+
+// Map is quarter res / sixteenth size
+static const size_t kMapDimensionScaleFactor = 4;
+// JPEG compress quality (0 ~ 100) for recovery map
+static const int kMapCompressQuality = 85;
+
+// TODO: fill in st2086 metadata
+static const st2086_metadata kSt2086Metadata = {
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ 0,
+ 1.0f,
+};
+
+/*
+ * Helper function used for generating XMP metadata.
+ *
+ * @param prefix The prefix part of the name.
+ * @param suffix The suffix part of the name.
+ * @return A name of the form "prefix:suffix".
+ */
+string Name(const string &prefix, const string &suffix) {
+ std::stringstream ss;
+ ss << prefix << ":" << suffix;
+ return ss.str();
+}
+
+/*
+ * Helper function used for writing data to destination.
+ *
+ * @param destination destination of the data to be written.
+ * @param source source of data being written.
+ * @param length length of the data to be written.
+ * @param position cursor in desitination where the data is to be written.
+ * @return status of succeed or error code.
+ */
+status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
+ if (position + length > destination->maxLength) {
+ return ERROR_JPEGR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
+ position += length;
+ return NO_ERROR;
+}
+
+/* Encode API-0 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr /* exif */) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (quality < 0 || quality > 100) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JpegEncoder jpeg_encoder;
+ // TODO: determine ICC data based on color gamut information
+ if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
+ uncompressed_yuv_420_image.width,
+ uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
+ return ERROR_JPEGR_ENCODE_ERROR;
+ }
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_encoder.getCompressedImagePtr();
+ jpeg.length = jpeg_encoder.getCompressedImageSize();
+
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+/* Encode API-1 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr /* exif */) {
+ if (uncompressed_p010_image == nullptr
+ || uncompressed_yuv_420_image == nullptr
+ || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (quality < 0 || quality > 100) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
+ || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JpegEncoder jpeg_encoder;
+ // TODO: determine ICC data based on color gamut information
+ if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
+ uncompressed_yuv_420_image->width,
+ uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
+ return ERROR_JPEGR_ENCODE_ERROR;
+ }
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_encoder.getCompressedImagePtr();
+ jpeg.length = jpeg_encoder.getCompressedImageSize();
+
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+/* Encode API-2 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest) {
+ if (uncompressed_p010_image == nullptr
+ || uncompressed_yuv_420_image == nullptr
+ || compressed_jpeg_image == nullptr
+ || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
+ || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+/* Encode API-3 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest) {
+ if (uncompressed_p010_image == nullptr
+ || compressed_jpeg_image == nullptr
+ || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+ jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
+ uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
+ uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
+ uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
+
+ if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
+ || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+ jr_info_ptr jpegr_info) {
+ if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ jpegr_compressed_struct primary_image, recovery_map;
+ JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
+ &primary_image, &recovery_map));
+
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
+ &jpegr_info->width, &jpegr_info->height,
+ jpegr_info->iccData, jpegr_info->exifData)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+/* Decode API */
+status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
+ jr_uncompressed_ptr dest,
+ jr_exif_ptr exif,
+ bool request_sdr) {
+ if (compressed_jpegr_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ // TODO: fill EXIF data
+ (void) exif;
+
+ jpegr_compressed_struct compressed_map;
+ jpegr_metadata metadata;
+ JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map));
+
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
+ uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
+ uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
+
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
+ jpeg_decoder.getXMPSize(), &metadata)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ if (request_sdr) {
+ memcpy(dest->data, uncompressed_yuv_420_image.data,
+ uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
+ dest->width = uncompressed_yuv_420_image.width;
+ dest->height = uncompressed_yuv_420_image.height;
+ } else {
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+ }
+
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map,
+ jr_uncompressed_ptr dest) {
+ if (compressed_recovery_map == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.decompressImage(compressed_recovery_map->data,
+ compressed_recovery_map->length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ dest->data = jpeg_decoder.getDecompressedImagePtr();
+ dest->width = jpeg_decoder.getDecompressedImageWidth();
+ dest->height = jpeg_decoder.getDecompressedImageHeight();
+
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_compressed_ptr dest) {
+ if (uncompressed_recovery_map == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ // TODO: should we have ICC data for the map?
+ JpegEncoder jpeg_encoder;
+ if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
+ uncompressed_recovery_map->width,
+ uncompressed_recovery_map->height,
+ kMapCompressQuality,
+ nullptr,
+ 0,
+ true /* isSingleChannel */)) {
+ return ERROR_JPEGR_ENCODE_ERROR;
+ }
+
+ if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
+ return ERROR_JPEGR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
+ dest->length = jpeg_encoder.getCompressedImageSize();
+ dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
+
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_p010_image,
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_yuv_420_image == nullptr
+ || uncompressed_p010_image == nullptr
+ || metadata == nullptr
+ || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
+ || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
+ return ERROR_JPEGR_RESOLUTION_MISMATCH;
+ }
+
+ if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
+ || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
+ size_t image_width = uncompressed_yuv_420_image->width;
+ size_t image_height = uncompressed_yuv_420_image->height;
+ size_t map_width = image_width / kMapDimensionScaleFactor;
+ size_t map_height = image_height / kMapDimensionScaleFactor;
+
+ dest->width = map_width;
+ dest->height = map_height;
+ dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
+ dest->data = new uint8_t[map_width * map_height];
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
+
+ ColorTransformFn hdrInvOetf = nullptr;
+ float hdr_white_nits = 0.0f;
+ switch (metadata->transferFunction) {
+ case JPEGR_TF_HLG:
+ hdrInvOetf = hlgInvOetf;
+ hdr_white_nits = kHlgMaxNits;
+ break;
+ case JPEGR_TF_PQ:
+ hdrInvOetf = pqInvOetf;
+ hdr_white_nits = kPqMaxNits;
+ break;
+ }
+
+ ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
+ uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
+
+ ColorCalculationFn luminanceFn = nullptr;
+ switch (uncompressed_yuv_420_image->colorGamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ luminanceFn = srgbLuminance;
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ luminanceFn = p3Luminance;
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ luminanceFn = bt2100Luminance;
+ break;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ // Should be impossible to hit after input validation.
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
+ float hdr_y_nits_max = 0.0f;
+ double hdr_y_nits_avg = 0.0f;
+ for (size_t y = 0; y < image_height; ++y) {
+ for (size_t x = 0; x < image_width; ++x) {
+ Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
+ Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
+ hdr_rgb = hdrGamutConversionFn(hdr_rgb);
+ float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
+
+ hdr_y_nits_avg += hdr_y_nits;
+ if (hdr_y_nits > hdr_y_nits_max) {
+ hdr_y_nits_max = hdr_y_nits;
+ }
+ }
+ }
+ hdr_y_nits_avg /= image_width * image_height;
+
+ metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
+ if (metadata->transferFunction == JPEGR_TF_PQ) {
+ metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
+ metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
+ }
+
+ for (size_t y = 0; y < map_height; ++y) {
+ for (size_t x = 0; x < map_width; ++x) {
+ Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
+ kMapDimensionScaleFactor, x, y);
+ Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
+ Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
+ float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
+
+ Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
+ Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
+ hdr_rgb = hdrGamutConversionFn(hdr_rgb);
+ float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
+
+ size_t pixel_idx = x + y * map_width;
+ reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
+ encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
+ }
+ }
+
+ map_data.release();
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_yuv_420_image == nullptr
+ || uncompressed_recovery_map == nullptr
+ || metadata == nullptr
+ || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ size_t width = uncompressed_yuv_420_image->width;
+ size_t height = uncompressed_yuv_420_image->height;
+
+ dest->width = width;
+ dest->height = height;
+ size_t pixel_count = width * height;
+
+ ColorTransformFn hdrOetf = nullptr;
+ switch (metadata->transferFunction) {
+ case JPEGR_TF_HLG:
+ hdrOetf = hlgOetf;
+ break;
+ case JPEGR_TF_PQ:
+ hdrOetf = pqOetf;
+ break;
+ }
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; ++x) {
+ Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
+ Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
+ Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
+
+ // TODO: determine map scaling factor based on actual map dims
+ float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
+ Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
+
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
+
+ size_t pixel_idx = x + y * width;
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map) {
+ if (compressed_jpegr_image == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ MessageHandler msg_handler;
+ std::shared_ptr<DataSegment> seg =
+ DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
+ static_cast<const uint8_t*>(compressed_jpegr_image->data),
+ DataSegment::BufferDispositionPolicy::kDontDelete);
+ DataSegmentDataSource data_source(seg);
+ JpegInfoBuilder jpeg_info_builder;
+ jpeg_info_builder.SetImageLimit(2);
+ JpegScanner jpeg_scanner(&msg_handler);
+ jpeg_scanner.Run(&data_source, &jpeg_info_builder);
+ data_source.Reset();
+
+ if (jpeg_scanner.HasError()) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ const auto& jpeg_info = jpeg_info_builder.GetInfo();
+ const auto& image_ranges = jpeg_info.GetImageRanges();
+ if (image_ranges.empty()) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (image_ranges.size() != 2) {
+ // Must be 2 JPEG Images
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (primary_image != nullptr) {
+ primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+ image_ranges[0].GetBegin();
+ primary_image->length = image_ranges[0].GetLength();
+ }
+
+ if (recovery_map != nullptr) {
+ recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+ image_ranges[1].GetBegin();
+ recovery_map->length = image_ranges[1].GetLength();
+ }
+
+ return NO_ERROR;
+}
+
+
+status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr dest) {
+ if (compressed_jpegr_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
+}
+
+status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
+ jr_compressed_ptr compressed_recovery_map,
+ jr_metadata_ptr metadata,
+ jr_compressed_ptr dest) {
+ if (compressed_jpeg_image == nullptr
+ || compressed_recovery_map == nullptr
+ || metadata == nullptr
+ || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
+ const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+
+ // 2 bytes: APP1 sign (ff e1)
+ // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
+ // x bytes: length of xmp packet
+
+ const int length = 3 + nameSpaceLength + xmp.size();
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+
+ int pos = 0;
+
+ // JPEG/R structure:
+ // SOI (ff d8)
+ // APP1 (ff e1)
+ // 2 bytes of length (2 + 29 + length of xmp packet)
+ // name space ("http://ns.adobe.com/xap/1.0/\0")
+ // xmp
+ // primary image (without the first two bytes, the SOI sign)
+ // secondary image (the recovery map)
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
+ JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
+ JPEGR_CHECK(Write(dest,
+ (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
+ JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
+ dest->length = pos;
+
+ return NO_ERROR;
+}
+
+string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+ const string kContainerPrefix = "GContainer";
+ const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
+ const string kItemPrefix = "Item";
+ const string kRecoveryMap = "RecoveryMap";
+ const string kDirectory = "Directory";
+ const string kImageJpeg = "image/jpeg";
+ const string kItem = "Item";
+ const string kLength = "Length";
+ const string kMime = "Mime";
+ const string kPrimary = "Primary";
+ const string kSemantic = "Semantic";
+ const string kVersion = "Version";
+
+ const string kConDir = Name(kContainerPrefix, kDirectory);
+ const string kContainerItem = Name(kContainerPrefix, kItem);
+ const string kItemLength = Name(kItemPrefix, kLength);
+ const string kItemMime = Name(kItemPrefix, kMime);
+ const string kItemSemantic = Name(kItemPrefix, kSemantic);
+
+ const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kContainerPrefix, kContainerUri);
+ writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version);
+ writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"),
+ metadata.rangeScalingFactor);
+ // TODO: determine structure for hdr10 metadata
+ // TODO: write rest of metadata
+ writer.StartWritingElements(kConDirSeq);
+ size_t item_depth = writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.FinishWritingElementsToDepth(item_depth);
+ writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ dest->width = uncompressed_p010_image->width;
+ dest->height = uncompressed_p010_image->height;
+ unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
+ dest->data = dest_data.get();
+
+ // TODO: Tone map algorighm here.
+
+ return NO_ERROR;
+}
+
+} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
new file mode 100644
index 0000000..e838f43
--- /dev/null
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -0,0 +1,437 @@
+/*
+ * 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 <cmath>
+
+#include <jpegrecoverymap/recoverymapmath.h>
+
+namespace android::recoverymap {
+
+////////////////////////////////////////////////////////////////////////////////
+// sRGB transformations
+
+// See IEC 61966-2-1, Equation F.7.
+static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
+
+float srgbLuminance(Color e) {
+ return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
+}
+
+// See ECMA TR/98, Section 7.
+static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f;
+
+Color srgbYuvToRgb(Color e_gamma) {
+ return {{{ e_gamma.y + kSrgbRCr * e_gamma.v,
+ e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v,
+ e_gamma.y + kSrgbBCb * e_gamma.u }}};
+}
+
+// See ECMA TR/98, Section 7.
+static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f;
+static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f;
+static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f;
+
+Color srgbRgbToYuv(Color e_gamma) {
+ return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b,
+ kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b,
+ kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}};
+}
+
+// See IEC 61966-2-1, Equations F.5 and F.6.
+float srgbInvOetf(float e_gamma) {
+ if (e_gamma <= 0.04045f) {
+ return e_gamma / 12.92f;
+ } else {
+ return pow((e_gamma + 0.055f) / 1.055f, 2.4);
+ }
+}
+
+Color srgbInvOetf(Color e_gamma) {
+ return {{{ srgbInvOetf(e_gamma.r),
+ srgbInvOetf(e_gamma.g),
+ srgbInvOetf(e_gamma.b) }}};
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Display-P3 transformations
+
+// See SMPTE EG 432-1, Table 7-2.
+static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
+
+float p3Luminance(Color e) {
+ return kP3R * e.r + kP3G * e.g + kP3B * e.b;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// BT.2100 transformations - according to ITU-R BT.2100-2
+
+// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
+static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
+
+float bt2100Luminance(Color e) {
+ return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
+}
+
+// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
+static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
+
+Color bt2100RgbToYuv(Color e_gamma) {
+ float y_gamma = bt2100Luminance(e_gamma);
+ return {{{ y_gamma,
+ (e_gamma.b - y_gamma) / kBt2100Cb,
+ (e_gamma.r - y_gamma) / kBt2100Cr }}};
+}
+
+// Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty
+// straight forward; we just invert the formulas for U and V above. But deriving
+// the formula for G is a bit more complicated:
+//
+// Start with equation for luminance:
+// Y = kBt2100R * R + kBt2100G * G + kBt2100B * B
+// Solve for G:
+// G = (Y - kBt2100R * R - kBt2100B * B) / kBt2100B
+// Substitute equations for R and B in terms YUV:
+// G = (Y - kBt2100R * (Y + kBt2100Cr * V) - kBt2100B * (Y + kBt2100Cb * U)) / kBt2100B
+// Simplify:
+// G = Y * ((1 - kBt2100R - kBt2100B) / kBt2100G)
+// + U * (kBt2100B * kBt2100Cb / kBt2100G)
+// + V * (kBt2100R * kBt2100Cr / kBt2100G)
+//
+// We then get the following coeficients for calculating G from YUV:
+//
+// Coef for Y = (1 - kBt2100R - kBt2100B) / kBt2100G = 1
+// Coef for U = kBt2100B * kBt2100Cb / kBt2100G = kBt2100GCb = ~0.1645
+// Coef for V = kBt2100R * kBt2100Cr / kBt2100G = kBt2100GCr = ~0.5713
+
+static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G;
+static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G;
+
+Color bt2100YuvToRgb(Color e_gamma) {
+ return {{{ e_gamma.y + kBt2100Cr * e_gamma.v,
+ e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v,
+ e_gamma.y + kBt2100Cb * e_gamma.u }}};
+}
+
+// See ITU-R BT.2100-2, Table 5, HLG Reference OETF.
+static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
+
+float hlgOetf(float e) {
+ if (e <= 1.0f/12.0f) {
+ return sqrt(3.0f * e);
+ } else {
+ return kHlgA * log(12.0f * e - kHlgB) + kHlgC;
+ }
+}
+
+Color hlgOetf(Color e) {
+ return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
+}
+
+// See ITU-R BT.2100-2, Table 5, HLG Reference EOTF.
+float hlgInvOetf(float e_gamma) {
+ if (e_gamma <= 0.5f) {
+ return pow(e_gamma, 2.0f) / 3.0f;
+ } else {
+ return (exp((e_gamma - kHlgC) / kHlgA) + kHlgB) / 12.0f;
+ }
+}
+
+Color hlgInvOetf(Color e_gamma) {
+ return {{{ hlgInvOetf(e_gamma.r),
+ hlgInvOetf(e_gamma.g),
+ hlgInvOetf(e_gamma.b) }}};
+}
+
+// See ITU-R BT.2100-2, Table 4, Reference PQ OETF.
+static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
+static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
+ kPqC3 = 2392.0f / 4096.0f * 32.0f;
+
+float pqOetf(float e) {
+ if (e <= 0.0f) return 0.0f;
+ return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)),
+ kPqM2);
+}
+
+Color pqOetf(Color e) {
+ return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
+}
+
+// Derived from the inverse of the Reference PQ OETF.
+static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f,
+ kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f;
+
+float pqInvOetf(float e_gamma) {
+ // This equation blows up if e_gamma is 0.0, and checking on <= 0.0 doesn't
+ // always catch 0.0. So, check on 0.0001, since anything this small will
+ // effectively be crushed to zero anyways.
+ if (e_gamma <= 0.0001f) return 0.0f;
+ return pow((kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB)
+ / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)),
+ kPqInvE);
+}
+
+Color pqInvOetf(Color e_gamma) {
+ return {{{ pqInvOetf(e_gamma.r),
+ pqInvOetf(e_gamma.g),
+ pqInvOetf(e_gamma.b) }}};
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Color conversions
+
+Color bt709ToP3(Color e) {
+ return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
+ 0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
+ 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}};
+}
+
+Color bt709ToBt2100(Color e) {
+ return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
+ 0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
+ 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}};
+}
+
+Color p3ToBt709(Color e) {
+ return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
+ -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
+ -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}};
+}
+
+Color p3ToBt2100(Color e) {
+ return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
+ 0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
+ -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}};
+}
+
+Color bt2100ToBt709(Color e) {
+ return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
+ -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
+ -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}};
+}
+
+Color bt2100ToP3(Color e) {
+ return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
+ -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
+ 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b
+ }}};
+}
+
+// TODO: confirm we always want to convert like this before calculating
+// luminance.
+ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) {
+ switch (sdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_P3:
+ return p3ToBt709;
+ case JPEGR_COLORGAMUT_BT2100:
+ return bt2100ToBt709;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return bt709ToP3;
+ case JPEGR_COLORGAMUT_P3:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_BT2100:
+ return bt2100ToP3;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return bt709ToBt2100;
+ case JPEGR_COLORGAMUT_P3:
+ return p3ToBt2100;
+ case JPEGR_COLORGAMUT_BT2100:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Recovery map calculations
+
+uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) {
+ float gain = 1.0f;
+ if (y_sdr > 0.0f) {
+ gain = y_hdr / y_sdr;
+ }
+
+ if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio;
+ if (gain > hdr_ratio) gain = hdr_ratio;
+
+ return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f);
+}
+
+static float applyRecovery(float e, float recovery, float hdr_ratio) {
+ if (e <= 0.0f) return 0.0f;
+ return exp2(log2(e) + recovery * log2(hdr_ratio));
+}
+
+Color applyRecovery(Color e, float recovery, float hdr_ratio) {
+ return {{{ applyRecovery(e.r, recovery, hdr_ratio),
+ applyRecovery(e.g, recovery, hdr_ratio),
+ applyRecovery(e.b, recovery, hdr_ratio) }}};
+}
+
+Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
+ size_t pixel_count = image->width * image->height;
+
+ size_t pixel_y_idx = x + y * image->width;
+ size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
+
+ uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y_idx];
+ uint8_t u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx];
+ uint8_t v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx];
+
+ // 128 bias for UV given we are using jpeglib; see:
+ // https://github.com/kornelski/libjpeg/blob/master/structure.doc
+ return {{{ static_cast<float>(y_uint) / 255.0f,
+ (static_cast<float>(u_uint) - 128.0f) / 255.0f,
+ (static_cast<float>(v_uint) - 128.0f) / 255.0f }}};
+}
+
+Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
+ size_t pixel_count = image->width * image->height;
+
+ size_t pixel_y_idx = x + y * image->width;
+ size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
+
+ uint16_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_y_idx]
+ >> 6;
+ uint16_t u_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2]
+ >> 6;
+ uint16_t v_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2 + 1]
+ >> 6;
+
+ // Conversions include taking narrow-range into account.
+ return {{{ static_cast<float>(y_uint) / 940.0f,
+ (static_cast<float>(u_uint) - 64.0f) / 940.0f - 0.5f,
+ (static_cast<float>(v_uint) - 64.0f) / 940.0f - 0.5f }}};
+}
+
+typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t);
+
+static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
+ getPixelFn get_pixel_fn) {
+ Color e = {{{ 0.0f, 0.0f, 0.0f }}};
+ for (size_t dy = 0; dy < map_scale_factor; ++dy) {
+ for (size_t dx = 0; dx < map_scale_factor; ++dx) {
+ e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
+ }
+ }
+
+ return e / static_cast<float>(map_scale_factor * map_scale_factor);
+}
+
+Color sampleYuv420(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
+ return samplePixels(image, map_scale_factor, x, y, getYuv420Pixel);
+}
+
+Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
+ return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
+}
+
+// TODO: do we need something more clever for filtering either the map or images
+// to generate the map?
+
+static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
+ return val < low ? low : (high < val ? high : val);
+}
+
+static float mapUintToFloat(uint8_t map_uint) {
+ return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
+}
+
+static float pythDistance(float x_diff, float y_diff) {
+ return sqrt(pow(x_diff, 2.0f) + pow(y_diff, 2.0f));
+}
+
+float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
+ float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
+ float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
+
+ size_t x_lower = static_cast<size_t>(floor(x_map));
+ size_t x_upper = x_lower + 1;
+ size_t y_lower = static_cast<size_t>(floor(y_map));
+ size_t y_upper = y_lower + 1;
+
+ x_lower = clamp(x_lower, 0, map->width - 1);
+ x_upper = clamp(x_upper, 0, map->width - 1);
+ y_lower = clamp(y_lower, 0, map->height - 1);
+ y_upper = clamp(y_upper, 0, map->height - 1);
+
+ // Use Shepard's method for inverse distance weighting. For more information:
+ // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
+
+ float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
+ float e1_dist = pythDistance(x_map - static_cast<float>(x_lower),
+ y_map - static_cast<float>(y_lower));
+ if (e1_dist == 0.0f) return e1;
+
+ float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
+ float e2_dist = pythDistance(x_map - static_cast<float>(x_lower),
+ y_map - static_cast<float>(y_upper));
+ if (e2_dist == 0.0f) return e2;
+
+ float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
+ float e3_dist = pythDistance(x_map - static_cast<float>(x_upper),
+ y_map - static_cast<float>(y_lower));
+ if (e3_dist == 0.0f) return e3;
+
+ float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
+ float e4_dist = pythDistance(x_map - static_cast<float>(x_upper),
+ y_map - static_cast<float>(y_upper));
+ if (e4_dist == 0.0f) return e2;
+
+ float e1_weight = 1.0f / e1_dist;
+ float e2_weight = 1.0f / e2_dist;
+ float e3_weight = 1.0f / e3_dist;
+ float e4_weight = 1.0f / e4_dist;
+ float total_weight = e1_weight + e2_weight + e3_weight + e4_weight;
+
+ return e1 * (e1_weight / total_weight)
+ + e2 * (e2_weight / total_weight)
+ + e3 * (e3_weight / total_weight)
+ + e4 * (e4_weight / total_weight);
+}
+
+uint32_t colorToRgba1010102(Color e_gamma) {
+ return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
+ | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
+ | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20)
+ | (0x3 << 30); // Set alpha to 1.0
+}
+
+} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
new file mode 100644
index 0000000..fe46cba
--- /dev/null
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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 <jpegrecoverymap/recoverymaputils.h>
+#include <jpegrecoverymap/recoverymap.h>
+#include <image_io/xml/xml_reader.h>
+#include <image_io/base/message_handler.h>
+#include <image_io/xml/xml_element_rules.h>
+#include <image_io/xml/xml_handler.h>
+#include <image_io/xml/xml_rule.h>
+
+#include <string>
+#include <sstream>
+
+using namespace photos_editing_formats::image_io;
+using namespace std;
+
+namespace android::recoverymap {
+
+
+// Extremely simple XML Handler - just searches for interesting elements
+class XMPXmlHandler : public XmlHandler {
+public:
+
+ XMPXmlHandler() : XmlHandler() {
+ rangeScalingFactorState = NotStrarted;
+ }
+
+ enum ParseState {
+ NotStrarted,
+ Started,
+ Done
+ };
+
+ virtual DataMatchResult StartElement(const XmlTokenContext& context) {
+ string val;
+ if (context.BuildTokenValue(&val)) {
+ if (!val.compare(rangeScalingFactorName)) {
+ rangeScalingFactorState = Started;
+ } else {
+ if (rangeScalingFactorState != Done) {
+ rangeScalingFactorState = NotStrarted;
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
+ if (rangeScalingFactorState == Started) {
+ rangeScalingFactorState = Done;
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult ElementContent(const XmlTokenContext& context) {
+ string val;
+ if (rangeScalingFactorState == Started) {
+ if (context.BuildTokenValue(&val)) {
+ rangeScalingFactorStr.assign(val);
+ }
+ }
+ return context.GetResult();
+ }
+
+ bool getRangeScalingFactor(float* scaling_factor) {
+ if (rangeScalingFactorState == Done) {
+ stringstream ss(rangeScalingFactorStr);
+ float val;
+ if (ss >> val) {
+ *scaling_factor = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getTransferFunction(jpegr_transfer_function* transfer_function) {
+ *transfer_function = JPEGR_TF_HLG;
+ return true;
+ }
+
+private:
+ static const string rangeScalingFactorName;
+ string rangeScalingFactorStr;
+ ParseState rangeScalingFactorState;
+};
+
+const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor";
+
+
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
+ string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+
+ if (xmp_size < nameSpace.size()+2) {
+ // Data too short
+ return false;
+ }
+
+ if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
+ // Not correct namespace
+ return false;
+ }
+
+ // Position the pointers to the start of XMP XML portion
+ xmp_data += nameSpace.size()+1;
+ xmp_size -= nameSpace.size()+1;
+ XMPXmlHandler handler;
+
+ // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
+ while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
+ xmp_size--;
+ }
+
+ string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
+ MessageHandler msg_handler;
+ unique_ptr<XmlRule> rule(new XmlElementRule);
+ XmlReader reader(&handler, &msg_handler);
+ reader.StartParse(std::move(rule));
+ reader.Parse(str);
+ reader.FinishParse();
+ if (reader.HasErrors()) {
+ // Parse error
+ return false;
+ }
+
+ if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
+ return false;
+ }
+
+ if (!handler.getTransferFunction(&metadata->transferFunction)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace android::recoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
new file mode 100644
index 0000000..b509478
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -0,0 +1,75 @@
+// 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.
+
+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_test {
+ name: "libjpegrecoverymap_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "recoverymap_test.cpp",
+ "recoverymapmath_test.cpp",
+ ],
+ shared_libs: [
+ "libjpeg",
+ "liblog",
+ ],
+ static_libs: [
+ "libimage_io",
+ "libgmock",
+ "libgtest",
+ "libjpegdecoder",
+ "libjpegencoder",
+ "libjpegrecoverymap",
+ ],
+}
+
+cc_test {
+ name: "libjpegencoder_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "jpegencoder_test.cpp",
+ ],
+ shared_libs: [
+ "libjpeg",
+ "liblog",
+ ],
+ static_libs: [
+ "libjpegencoder",
+ "libgtest",
+ ],
+}
+
+cc_test {
+ name: "libjpegdecoder_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "jpegdecoder_test.cpp",
+ ],
+ shared_libs: [
+ "libjpeg",
+ "liblog",
+ ],
+ static_libs: [
+ "libjpegdecoder",
+ "libgtest",
+ ],
+}
diff --git a/libs/jpegrecoverymap/tests/data/jpeg_image.jpg b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg
new file mode 100644
index 0000000..e285742
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12 b/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12
new file mode 100644
index 0000000..7b2fc71
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-318x240.yu12
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg b/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg
new file mode 100644
index 0000000..20b5a2c
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240-y.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg b/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg
new file mode 100644
index 0000000..41300f4
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240-yuv.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.y b/libs/jpegrecoverymap/tests/data/minnie-320x240.y
new file mode 100644
index 0000000..f9d8371
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240.y
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12 b/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12
new file mode 100644
index 0000000..0d66f53
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/minnie-320x240.yu12
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010
new file mode 100644
index 0000000..01673bf
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp b/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp
new file mode 100644
index 0000000..8e01351
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/jpegdecoder_test.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 <jpegrecoverymap/jpegdecoder.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <fcntl.h>
+
+namespace android::recoverymap {
+
+#define YUV_IMAGE "/sdcard/Documents/minnie-320x240-yuv.jpg"
+#define YUV_IMAGE_SIZE 20193
+#define GREY_IMAGE "/sdcard/Documents/minnie-320x240-y.jpg"
+#define GREY_IMAGE_SIZE 20193
+
+class JpegDecoderTest : public testing::Test {
+public:
+ struct Image {
+ std::unique_ptr<uint8_t[]> buffer;
+ size_t size;
+ };
+ JpegDecoderTest();
+ ~JpegDecoderTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Image mYuvImage, mGreyImage;
+};
+
+JpegDecoderTest::JpegDecoderTest() {}
+
+JpegDecoderTest::~JpegDecoderTest() {}
+
+static size_t getFileSize(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ ALOGW("%s : fstat failed", __func__);
+ return 0;
+ }
+ return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], JpegDecoderTest::Image* result) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ int length = getFileSize(fd);
+ if (length == 0) {
+ close(fd);
+ return false;
+ }
+ result->buffer.reset(new uint8_t[length]);
+ if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+void JpegDecoderTest::SetUp() {
+ if (!loadFile(YUV_IMAGE, &mYuvImage)) {
+ FAIL() << "Load file " << YUV_IMAGE << " failed";
+ }
+ mYuvImage.size = YUV_IMAGE_SIZE;
+ if (!loadFile(GREY_IMAGE, &mGreyImage)) {
+ FAIL() << "Load file " << GREY_IMAGE << " failed";
+ }
+ mGreyImage.size = GREY_IMAGE_SIZE;
+}
+
+void JpegDecoderTest::TearDown() {}
+
+TEST_F(JpegDecoderTest, decodeYuvImage) {
+ JpegDecoder decoder;
+ EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+TEST_F(JpegDecoderTest, decodeGreyImage) {
+ JpegDecoder decoder;
+ EXPECT_TRUE(decoder.decompressImage(mGreyImage.buffer.get(), mGreyImage.size));
+ ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+}
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/jpegencoder_test.cpp b/libs/jpegrecoverymap/tests/jpegencoder_test.cpp
new file mode 100644
index 0000000..4cd2a5e
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/jpegencoder_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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 <jpegrecoverymap/jpegencoder.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <fcntl.h>
+
+namespace android::recoverymap {
+
+#define VALID_IMAGE "/sdcard/Documents/minnie-320x240.yu12"
+#define VALID_IMAGE_WIDTH 320
+#define VALID_IMAGE_HEIGHT 240
+#define SINGLE_CHANNEL_IMAGE "/sdcard/Documents/minnie-320x240.y"
+#define SINGLE_CHANNEL_IMAGE_WIDTH VALID_IMAGE_WIDTH
+#define SINGLE_CHANNEL_IMAGE_HEIGHT VALID_IMAGE_HEIGHT
+#define INVALID_SIZE_IMAGE "/sdcard/Documents/minnie-318x240.yu12"
+#define INVALID_SIZE_IMAGE_WIDTH 318
+#define INVALID_SIZE_IMAGE_HEIGHT 240
+#define JPEG_QUALITY 90
+
+class JpegEncoderTest : public testing::Test {
+public:
+ struct Image {
+ std::unique_ptr<uint8_t[]> buffer;
+ size_t width;
+ size_t height;
+ };
+ JpegEncoderTest();
+ ~JpegEncoderTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ Image mValidImage, mInvalidSizeImage, mSingleChannelImage;
+};
+
+JpegEncoderTest::JpegEncoderTest() {}
+
+JpegEncoderTest::~JpegEncoderTest() {}
+
+static size_t getFileSize(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ ALOGW("%s : fstat failed", __func__);
+ return 0;
+ }
+ return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], JpegEncoderTest::Image* result) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ int length = getFileSize(fd);
+ if (length == 0) {
+ close(fd);
+ return false;
+ }
+ result->buffer.reset(new uint8_t[length]);
+ if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+void JpegEncoderTest::SetUp() {
+ if (!loadFile(VALID_IMAGE, &mValidImage)) {
+ FAIL() << "Load file " << VALID_IMAGE << " failed";
+ }
+ mValidImage.width = VALID_IMAGE_WIDTH;
+ mValidImage.height = VALID_IMAGE_HEIGHT;
+ if (!loadFile(INVALID_SIZE_IMAGE, &mInvalidSizeImage)) {
+ FAIL() << "Load file " << INVALID_SIZE_IMAGE << " failed";
+ }
+ mInvalidSizeImage.width = INVALID_SIZE_IMAGE_WIDTH;
+ mInvalidSizeImage.height = INVALID_SIZE_IMAGE_HEIGHT;
+ if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
+ FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
+ }
+ mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
+ mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
+}
+
+void JpegEncoderTest::TearDown() {}
+
+TEST_F(JpegEncoderTest, validImage) {
+ JpegEncoder encoder;
+ EXPECT_TRUE(encoder.compressImage(mValidImage.buffer.get(), mValidImage.width,
+ mValidImage.height, JPEG_QUALITY, NULL, 0));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+TEST_F(JpegEncoderTest, invalidSizeImage) {
+ JpegEncoder encoder;
+ EXPECT_FALSE(encoder.compressImage(mInvalidSizeImage.buffer.get(), mInvalidSizeImage.width,
+ mInvalidSizeImage.height, JPEG_QUALITY, NULL, 0));
+}
+
+TEST_F(JpegEncoderTest, singleChannelImage) {
+ JpegEncoder encoder;
+ EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width,
+ mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true));
+ ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
+}
+
+}
+
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
new file mode 100644
index 0000000..0f96723
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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 <jpegrecoverymap/recoverymap.h>
+#include <fcntl.h>
+#include <fstream>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
+#define RAW_P010_IMAGE_WIDTH 1280
+#define RAW_P010_IMAGE_HEIGHT 720
+#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
+
+#define SAVE_ENCODING_RESULT true
+#define SAVE_DECODING_RESULT true
+
+namespace android::recoverymap {
+
+class RecoveryMapTest : public testing::Test {
+public:
+ RecoveryMapTest();
+ ~RecoveryMapTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ struct jpegr_uncompressed_struct mRawP010Image;
+ struct jpegr_compressed_struct mJpegImage;
+};
+
+RecoveryMapTest::RecoveryMapTest() {}
+RecoveryMapTest::~RecoveryMapTest() {}
+
+void RecoveryMapTest::SetUp() {}
+void RecoveryMapTest::TearDown() {
+ free(mRawP010Image.data);
+ free(mJpegImage.data);
+}
+
+static size_t getFileSize(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ ALOGW("%s : fstat failed", __func__);
+ return 0;
+ }
+ return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], void*& result, int* fileLength) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ int length = getFileSize(fd);
+ if (length == 0) {
+ close(fd);
+ return false;
+ }
+ if (fileLength != nullptr) {
+ *fileLength = length;
+ }
+ result = malloc(length);
+ if (read(fd, result, length) != static_cast<ssize_t>(length)) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+TEST_F(RecoveryMapTest, build) {
+ // Force all of the recovery map lib to be linked by calling all public functions.
+ RecoveryMap recovery_map;
+ recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+ nullptr, 0, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+ nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
+ recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
+}
+
+TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
+ mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
+ mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
+ FAIL() << "Load file " << JPEG_IMAGE << " failed";
+ }
+ mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
new file mode 100644
index 0000000..169201c
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -0,0 +1,882 @@
+/*
+ * 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 <cmath>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <jpegrecoverymap/recoverymapmath.h>
+
+namespace android::recoverymap {
+
+class RecoveryMapMathTest : public testing::Test {
+public:
+ RecoveryMapMathTest();
+ ~RecoveryMapMathTest();
+
+ float ComparisonEpsilon() { return 1e-4f; }
+ float LuminanceEpsilon() { return 1e-2f; }
+
+ Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
+ return {{{ static_cast<float>(y) / 255.0f,
+ (static_cast<float>(u) - 128.0f) / 255.0f,
+ (static_cast<float>(v) - 128.0f) / 255.0f }}};
+ }
+
+ Color P010(uint16_t y, uint16_t u, uint16_t v) {
+ return {{{ static_cast<float>(y) / 940.0f,
+ (static_cast<float>(u) - 64.0f) / 940.0f - 0.5f,
+ (static_cast<float>(v) - 64.0f) / 940.0f - 0.5f }}};
+ }
+
+ float Map(uint8_t e) {
+ return (static_cast<float>(e) - 127.5f) / 127.5f;
+ }
+
+ Color ColorMin(Color e1, Color e2) {
+ return {{{ fmin(e1.r, e2.r), fmin(e1.g, e2.g), fmin(e1.b, e2.b) }}};
+ }
+
+ Color ColorMax(Color e1, Color e2) {
+ return {{{ fmax(e1.r, e2.r), fmax(e1.g, e2.g), fmax(e1.b, e2.b) }}};
+ }
+
+ Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
+ Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; }
+
+ Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+ Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; }
+ Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; }
+
+ Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
+ Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+
+ Color SrgbYuvRed() { return {{{ 0.299f, -0.1687f, 0.5f }}}; }
+ Color SrgbYuvGreen() { return {{{ 0.587f, -0.3313f, -0.4187f }}}; }
+ Color SrgbYuvBlue() { return {{{ 0.114f, 0.5f, -0.0813f }}}; }
+
+ Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
+ Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
+ Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; }
+
+ float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
+ Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
+ Color rgb = srgbInvOetf(rgb_gamma);
+ float luminance_scaled = luminanceFn(rgb);
+ return luminance_scaled * kSdrWhiteNits;
+ }
+
+ float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
+ ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
+ float scale_factor) {
+ Color rgb_gamma = bt2100YuvToRgb(yuv_gamma);
+ Color rgb = hdrInvOetf(rgb_gamma);
+ rgb = gamutConversionFn(rgb);
+ float luminance_scaled = luminanceFn(rgb);
+ return luminance_scaled * scale_factor;
+ }
+
+ Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
+ Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
+ Color rgb = srgbInvOetf(rgb_gamma);
+ return applyRecovery(rgb, recovery, range_scaling_factor);
+ }
+
+ jpegr_uncompressed_struct Yuv420Image() {
+ static uint8_t pixels[] = {
+ // Y
+ 0x00, 0x10, 0x20, 0x30,
+ 0x01, 0x11, 0x21, 0x31,
+ 0x02, 0x12, 0x22, 0x32,
+ 0x03, 0x13, 0x23, 0x33,
+ // U
+ 0xA0, 0xA1,
+ 0xA2, 0xA3,
+ // V
+ 0xB0, 0xB1,
+ 0xB2, 0xB3,
+ };
+ return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+ }
+
+ Color (*Yuv420Colors())[4] {
+ static Color colors[4][4] = {
+ {
+ Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0),
+ Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1),
+ }, {
+ Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0),
+ Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1),
+ }, {
+ Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2),
+ Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3),
+ }, {
+ Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2),
+ Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3),
+ },
+ };
+ return colors;
+ }
+
+ jpegr_uncompressed_struct P010Image() {
+ static uint16_t pixels[] = {
+ // Y
+ 0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6,
+ 0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6,
+ 0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6,
+ 0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6,
+ // UV
+ 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
+ 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
+ };
+ return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+ }
+
+ Color (*P010Colors())[4] {
+ static Color colors[4][4] = {
+ {
+ P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0),
+ P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1),
+ }, {
+ P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0),
+ P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1),
+ }, {
+ P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2),
+ P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3),
+ }, {
+ P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2),
+ P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3),
+ },
+ };
+ return colors;
+ }
+
+ jpegr_uncompressed_struct MapImage() {
+ static uint8_t pixels[] = {
+ 0x00, 0x10, 0x20, 0x30,
+ 0x01, 0x11, 0x21, 0x31,
+ 0x02, 0x12, 0x22, 0x32,
+ 0x03, 0x13, 0x23, 0x33,
+ };
+ return { pixels, 4, 4, JPEGR_COLORGAMUT_UNSPECIFIED };
+ }
+
+ float (*MapValues())[4] {
+ static float values[4][4] = {
+ {
+ Map(0x00), Map(0x10), Map(0x20), Map(0x30),
+ }, {
+ Map(0x01), Map(0x11), Map(0x21), Map(0x31),
+ }, {
+ Map(0x02), Map(0x12), Map(0x22), Map(0x32),
+ }, {
+ Map(0x03), Map(0x13), Map(0x23), Map(0x33),
+ },
+ };
+ return values;
+ }
+
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+};
+
+RecoveryMapMathTest::RecoveryMapMathTest() {}
+RecoveryMapMathTest::~RecoveryMapMathTest() {}
+
+void RecoveryMapMathTest::SetUp() {}
+void RecoveryMapMathTest::TearDown() {}
+
+#define EXPECT_RGB_EQ(e1, e2) \
+ EXPECT_FLOAT_EQ((e1).r, (e2).r); \
+ EXPECT_FLOAT_EQ((e1).g, (e2).g); \
+ EXPECT_FLOAT_EQ((e1).b, (e2).b)
+
+#define EXPECT_RGB_NEAR(e1, e2) \
+ EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon())
+
+#define EXPECT_RGB_CLOSE(e1, e2) \
+ EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \
+ EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
+ EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
+
+#define EXPECT_YUV_EQ(e1, e2) \
+ EXPECT_FLOAT_EQ((e1).y, (e2).y); \
+ EXPECT_FLOAT_EQ((e1).u, (e2).u); \
+ EXPECT_FLOAT_EQ((e1).v, (e2).v)
+
+#define EXPECT_YUV_NEAR(e1, e2) \
+ EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon())
+
+#define EXPECT_YUV_BETWEEN(e, min, max) \
+ EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y), testing::Le((max).y))); \
+ EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u), testing::Le((max).u))); \
+ EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v), testing::Le((max).v)))
+
+// TODO: a bunch of these tests can be parameterized.
+
+TEST_F(RecoveryMapMathTest, ColorConstruct) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ EXPECT_FLOAT_EQ(e1.r, 0.1f);
+ EXPECT_FLOAT_EQ(e1.g, 0.2f);
+ EXPECT_FLOAT_EQ(e1.b, 0.3f);
+
+ EXPECT_FLOAT_EQ(e1.y, 0.1f);
+ EXPECT_FLOAT_EQ(e1.u, 0.2f);
+ EXPECT_FLOAT_EQ(e1.v, 0.3f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorAddColor) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 + e1;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
+
+ e2 += e1;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorAddFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 + 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f);
+
+ e2 += 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorSubtractColor) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 - e1;
+ EXPECT_FLOAT_EQ(e2.r, 0.0f);
+ EXPECT_FLOAT_EQ(e2.g, 0.0f);
+ EXPECT_FLOAT_EQ(e2.b, 0.0f);
+
+ e2 -= e1;
+ EXPECT_FLOAT_EQ(e2.r, -e1.r);
+ EXPECT_FLOAT_EQ(e2.g, -e1.g);
+ EXPECT_FLOAT_EQ(e2.b, -e1.b);
+}
+
+TEST_F(RecoveryMapMathTest, ColorSubtractFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 - 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f);
+
+ e2 -= 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorMultiplyFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 * 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
+
+ e2 *= 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorDivideFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 / 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f);
+
+ e2 /= 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, SrgbLuminance) {
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
+}
+
+TEST_F(RecoveryMapMathTest, SrgbYuvToRgb) {
+ Color rgb_black = srgbYuvToRgb(YuvBlack());
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = srgbYuvToRgb(YuvWhite());
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = srgbYuvToRgb(SrgbYuvRed());
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = srgbYuvToRgb(SrgbYuvGreen());
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = srgbYuvToRgb(SrgbYuvBlue());
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbRgbToYuv) {
+ Color yuv_black = srgbRgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+ Color yuv_white = srgbRgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+ Color yuv_r = srgbRgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed());
+
+ Color yuv_g = srgbRgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen());
+
+ Color yuv_b = srgbRgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbRgbYuvRoundtrip) {
+ Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack()));
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite()));
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed()));
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen()));
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue()));
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbTransferFunction) {
+ EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f);
+ EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon());
+ EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon());
+ EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, P3Luminance) {
+ EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100Luminance) {
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100YuvToRgb) {
+ Color rgb_black = bt2100YuvToRgb(YuvBlack());
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = bt2100YuvToRgb(YuvWhite());
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed());
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen());
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue());
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100RgbToYuv) {
+ Color yuv_black = bt2100RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+ Color yuv_white = bt2100RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+ Color yuv_r = bt2100RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed());
+
+ Color yuv_g = bt2100RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen());
+
+ Color yuv_b = bt2100RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue());
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100RgbYuvRoundtrip) {
+ Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack()));
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite()));
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed()));
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen()));
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue()));
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, HlgOetf) {
+ EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
+ EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
+
+ Color e = {{{ 0.04167f, 0.08333f, 0.5f }}};
+ Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}};
+ EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
+}
+
+TEST_F(RecoveryMapMathTest, HlgInvOetf) {
+ EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f);
+ EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
+
+ Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}};
+ Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}};
+ EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
+}
+
+TEST_F(RecoveryMapMathTest, HlgTransferFunctionRoundtrip) {
+ EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f);
+ EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, PqOetf) {
+ EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f);
+ EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon());
+ EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon());
+ EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
+
+ Color e = {{{ 0.01f, 0.5f, 0.99f }}};
+ Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}};
+ EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
+}
+
+TEST_F(RecoveryMapMathTest, PqInvOetf) {
+ EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f);
+ EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
+
+ Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}};
+ Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}};
+ EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
+}
+
+TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) {
+ EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
+ EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorConversionLookup) {
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT709),
+ identityConversion);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_P3),
+ p3ToBt709);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT2100),
+ bt2100ToBt709);
+
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT709),
+ bt709ToP3);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_P3),
+ identityConversion);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT2100),
+ bt2100ToP3);
+
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT709),
+ bt709ToBt2100);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_P3),
+ p3ToBt2100);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT2100),
+ identityConversion);
+
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT709),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_P3),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT2100),
+ nullptr);
+}
+
+TEST_F(RecoveryMapMathTest, EncodeRecovery) {
+ EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127);
+ EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127);
+ EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0);
+ EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0);
+
+ EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127);
+ EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255);
+ EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255);
+ EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0);
+ EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0);
+ EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191);
+ EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63);
+
+ EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255);
+ EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0);
+ EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191);
+ EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63);
+
+ EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255);
+ EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0);
+ EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191);
+ EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63);
+}
+
+TEST_F(RecoveryMapMathTest, ApplyRecovery) {
+ EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack());
+ EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack());
+ EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack());
+
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite());
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f);
+
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite());
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f);
+
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite());
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f);
+
+ Color e = {{{ 0.0f, 0.5f, 1.0f }}};
+
+ EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f);
+ EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e);
+ EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, GetYuv420Pixel) {
+ jpegr_uncompressed_struct image = Yuv420Image();
+ Color (*colors)[4] = Yuv420Colors();
+
+ for (size_t y = 0; y < 4; ++y) {
+ for (size_t x = 0; x < 4; ++x) {
+ EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, GetP010Pixel) {
+ jpegr_uncompressed_struct image = P010Image();
+ Color (*colors)[4] = P010Colors();
+
+ for (size_t y = 0; y < 4; ++y) {
+ for (size_t x = 0; x < 4; ++x) {
+ EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, SampleYuv420) {
+ jpegr_uncompressed_struct image = Yuv420Image();
+ Color (*colors)[4] = Yuv420Colors();
+
+ static const size_t kMapScaleFactor = 2;
+ for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
+ for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
+ Color min = {{{ 1.0f, 1.0f, 1.0f }}};
+ Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+
+ for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
+ for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
+ Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
+ min = ColorMin(min, e);
+ max = ColorMax(max, e);
+ }
+ }
+
+ // Instead of reimplementing the sampling algorithm, confirm that the
+ // sample output is within the range of the min and max of the nearest
+ // points.
+ EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, SampleP010) {
+ jpegr_uncompressed_struct image = P010Image();
+ Color (*colors)[4] = P010Colors();
+
+ static const size_t kMapScaleFactor = 2;
+ for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
+ for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
+ Color min = {{{ 1.0f, 1.0f, 1.0f }}};
+ Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+
+ for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
+ for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
+ Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
+ min = ColorMin(min, e);
+ max = ColorMax(max, e);
+ }
+ }
+
+ // Instead of reimplementing the sampling algorithm, confirm that the
+ // sample output is within the range of the min and max of the nearest
+ // points.
+ EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, SampleMap) {
+ jpegr_uncompressed_struct image = MapImage();
+ float (*values)[4] = MapValues();
+
+ static const size_t kMapScaleFactor = 2;
+ for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
+ for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
+ size_t x_base = x / kMapScaleFactor;
+ size_t y_base = y / kMapScaleFactor;
+
+ float min = 1.0f;
+ float max = -1.0f;
+
+ min = fmin(min, values[y_base][x_base]);
+ max = fmax(max, values[y_base][x_base]);
+ if (y_base + 1 < 4) {
+ min = fmin(min, values[y_base + 1][x_base]);
+ max = fmax(max, values[y_base + 1][x_base]);
+ }
+ if (x_base + 1 < 4) {
+ min = fmin(min, values[y_base][x_base + 1]);
+ max = fmax(max, values[y_base][x_base + 1]);
+ }
+ if (y_base + 1 < 4 && x_base + 1 < 4) {
+ min = fmin(min, values[y_base + 1][x_base + 1]);
+ max = fmax(max, values[y_base + 1][x_base + 1]);
+ }
+
+ // Instead of reimplementing the sampling algorithm, confirm that the
+ // sample output is within the range of the min and max of the nearest
+ // points.
+ EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
+ testing::AllOf(testing::Ge(min), testing::Le(max)));
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, ColorToRgba1010102) {
+ EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
+ EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
+ EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
+ EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
+ EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
+
+ Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
+ EXPECT_EQ(colorToRgba1010102(e_gamma),
+ 0x3 << 30
+ | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff))
+ | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10
+ | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgb) {
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
+ 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance),
+ kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
+ srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
+ srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
+ srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbP3) {
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance),
+ 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance),
+ kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance),
+ p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
+ p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
+ p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbBt2100) {
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance),
+ 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance),
+ kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
+ bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
+ bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
+ bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceHlg) {
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ 0.0f);
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ kHlgMaxNits);
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) {
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ 0.0f);
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ kPqMaxNits);
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
+}
+
+//Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
+TEST_F(RecoveryMapMathTest, ApplyMap) {
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f),
+ RgbWhite() * 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f),
+ RgbRed() * 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f),
+ RgbGreen() * 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f),
+ RgbBlue() * 8.0f);
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f),
+ RgbWhite() * sqrt(8.0f));
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f),
+ RgbRed() * sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f),
+ RgbGreen() * sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f),
+ RgbBlue() * sqrt(8.0f));
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f),
+ RgbWhite());
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f),
+ RgbRed());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f),
+ RgbGreen());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f),
+ RgbBlue());
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f),
+ RgbWhite() / sqrt(8.0f));
+ EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f),
+ RgbRed() / sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f),
+ RgbGreen() / sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f),
+ RgbBlue() / sqrt(8.0f));
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f),
+ RgbWhite() / 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f),
+ RgbRed() / 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f),
+ RgbGreen() / 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f),
+ RgbBlue() / 8.0f);
+}
+
+} // namespace android::recoverymap
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 539cbaa..e64165f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-#define LOG_TAG "Choreographer"
-//#define LOG_NDEBUG 0
-
#include <android-base/thread_annotations.h>
#include <android/gui/ISurfaceComposer.h>
-#include <gui/DisplayEventDispatcher.h>
#include <jni.h>
+#include <nativedisplay/Choreographer.h>
#include <private/android/choreographer.h>
#include <utils/Looper.h>
#include <utils/Timers.h>
@@ -31,444 +28,9 @@
#include <queue>
#include <thread>
-namespace {
-struct {
- // Global JVM that is provided by zygote
- JavaVM* jvm = nullptr;
- struct {
- jclass clazz;
- jmethodID getInstance;
- jmethodID registerNativeChoreographerForRefreshRateCallbacks;
- jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
- } displayManagerGlobal;
-} gJni;
+#undef LOG_TAG
+#define LOG_TAG "AChoreographer"
-// Gets the JNIEnv* for this thread, and performs one-off initialization if we
-// have never retrieved a JNIEnv* pointer before.
-JNIEnv* getJniEnv() {
- if (gJni.jvm == nullptr) {
- ALOGW("AChoreographer: No JVM provided!");
- return nullptr;
- }
-
- JNIEnv* env = nullptr;
- if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGD("Attaching thread to JVM for AChoreographer");
- JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
- jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
- if (attachResult != JNI_OK) {
- ALOGE("Unable to attach thread. Error: %d", attachResult);
- return nullptr;
- }
- }
- if (env == nullptr) {
- ALOGW("AChoreographer: No JNI env available!");
- }
- return env;
-}
-
-inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-} // namespace
-
-namespace android {
-using gui::VsyncEventData;
-
-struct FrameCallback {
- AChoreographer_frameCallback callback;
- AChoreographer_frameCallback64 callback64;
- AChoreographer_vsyncCallback vsyncCallback;
- void* data;
- nsecs_t dueTime;
-
- inline bool operator<(const FrameCallback& rhs) const {
- // Note that this is intentionally flipped because we want callbacks due sooner to be at
- // the head of the queue
- return dueTime > rhs.dueTime;
- }
-};
-
-struct RefreshRateCallback {
- AChoreographer_refreshRateCallback callback;
- void* data;
- bool firstCallbackFired = false;
-};
-
-class Choreographer;
-
-/**
- * Implementation of AChoreographerFrameCallbackData.
- */
-struct ChoreographerFrameCallbackDataImpl {
- int64_t frameTimeNanos{0};
-
- VsyncEventData vsyncEventData;
-
- const Choreographer* choreographer;
-};
-
-struct {
- std::mutex lock;
- std::vector<Choreographer*> ptrs GUARDED_BY(lock);
- std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
- bool registeredToDisplayManager GUARDED_BY(lock) = false;
-
- std::atomic<nsecs_t> mLastKnownVsync = -1;
-} gChoreographers;
-
-class Choreographer : public DisplayEventDispatcher, public MessageHandler {
-public:
- explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
- void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64,
- AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay);
- void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
- EXCLUDES(gChoreographers.lock);
- void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
- // Drains the queue of pending vsync periods and dispatches refresh rate
- // updates to callbacks.
- // The assumption is that this method is only called on a single
- // processing thread, either by looper or by AChoreographer_handleEvents
- void handleRefreshRateUpdates();
- void scheduleLatestConfigRequest();
-
- enum {
- MSG_SCHEDULE_CALLBACKS = 0,
- MSG_SCHEDULE_VSYNC = 1,
- MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
- };
- virtual void handleMessage(const Message& message) override;
-
- static Choreographer* getForThread();
- virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
- int64_t getFrameInterval() const;
- bool inCallback() const;
-
-private:
- Choreographer(const Choreographer&) = delete;
-
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
- VsyncEventData vsyncEventData) override;
- void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
- nsecs_t vsyncPeriod) override;
- void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
- void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
- std::vector<FrameRateOverride> overrides) override;
-
- void scheduleCallbacks();
-
- ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
- void registerStartTime() const;
-
- std::mutex mLock;
- // Protected by mLock
- std::priority_queue<FrameCallback> mFrameCallbacks;
- std::vector<RefreshRateCallback> mRefreshRateCallbacks;
-
- nsecs_t mLatestVsyncPeriod = -1;
- VsyncEventData mLastVsyncEventData;
- bool mInCallback = false;
-
- const sp<Looper> mLooper;
- const std::thread::id mThreadId;
-
- // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
- static constexpr size_t kMaxStartTimes = 250;
-};
-
-static thread_local Choreographer* gChoreographer;
-Choreographer* Choreographer::getForThread() {
- if (gChoreographer == nullptr) {
- sp<Looper> looper = Looper::getForThread();
- if (!looper.get()) {
- ALOGW("No looper prepared for thread");
- return nullptr;
- }
- gChoreographer = new Choreographer(looper);
- status_t result = gChoreographer->initialize();
- if (result != OK) {
- ALOGW("Failed to initialize");
- return nullptr;
- }
- }
- return gChoreographer;
-}
-
-Choreographer::Choreographer(const sp<Looper>& looper)
- : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
- mLooper(looper),
- mThreadId(std::this_thread::get_id()) {
- std::lock_guard<std::mutex> _l(gChoreographers.lock);
- gChoreographers.ptrs.push_back(this);
-}
-
-Choreographer::~Choreographer() {
- std::lock_guard<std::mutex> _l(gChoreographers.lock);
- gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
- gChoreographers.ptrs.end(),
- [=](Choreographer* c) { return c == this; }),
- gChoreographers.ptrs.end());
- // Only poke DisplayManagerGlobal to unregister if we previously registered
- // callbacks.
- if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
- gChoreographers.registeredToDisplayManager = false;
- JNIEnv* env = getJniEnv();
- if (env == nullptr) {
- ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
- return;
- }
- jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
- gJni.displayManagerGlobal.getInstance);
- if (dmg == nullptr) {
- ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
- } else {
- env->CallVoidMethod(dmg,
- gJni.displayManagerGlobal
- .unregisterNativeChoreographerForRefreshRateCallbacks);
- env->DeleteLocalRef(dmg);
- }
- }
-}
-
-void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64,
- AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
- {
- std::lock_guard<std::mutex> _l{mLock};
- mFrameCallbacks.push(callback);
- }
- if (callback.dueTime <= now) {
- if (std::this_thread::get_id() != mThreadId) {
- if (mLooper != nullptr) {
- Message m{MSG_SCHEDULE_VSYNC};
- mLooper->sendMessage(this, m);
- } else {
- scheduleVsync();
- }
- } else {
- scheduleVsync();
- }
- } else {
- if (mLooper != nullptr) {
- Message m{MSG_SCHEDULE_CALLBACKS};
- mLooper->sendMessageDelayed(delay, this, m);
- } else {
- scheduleCallbacks();
- }
- }
-}
-
-void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
- std::lock_guard<std::mutex> _l{mLock};
- for (const auto& callback : mRefreshRateCallbacks) {
- // Don't re-add callbacks.
- if (cb == callback.callback && data == callback.data) {
- return;
- }
- }
- mRefreshRateCallbacks.emplace_back(
- RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
- bool needsRegistration = false;
- {
- std::lock_guard<std::mutex> _l2(gChoreographers.lock);
- needsRegistration = !gChoreographers.registeredToDisplayManager;
- }
- if (needsRegistration) {
- JNIEnv* env = getJniEnv();
- if (env == nullptr) {
- ALOGW("JNI environment is unavailable, skipping registration");
- return;
- }
- jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
- gJni.displayManagerGlobal.getInstance);
- if (dmg == nullptr) {
- ALOGW("DMS is not initialized yet: skipping registration");
- return;
- } else {
- env->CallVoidMethod(dmg,
- gJni.displayManagerGlobal
- .registerNativeChoreographerForRefreshRateCallbacks,
- reinterpret_cast<int64_t>(this));
- env->DeleteLocalRef(dmg);
- {
- std::lock_guard<std::mutex> _l2(gChoreographers.lock);
- gChoreographers.registeredToDisplayManager = true;
- }
- }
- } else {
- scheduleLatestConfigRequest();
- }
-}
-
-void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
- void* data) {
- std::lock_guard<std::mutex> _l{mLock};
- mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
- mRefreshRateCallbacks.end(),
- [&](const RefreshRateCallback& callback) {
- return cb == callback.callback &&
- data == callback.data;
- }),
- mRefreshRateCallbacks.end());
-}
-
-void Choreographer::scheduleLatestConfigRequest() {
- if (mLooper != nullptr) {
- Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
- mLooper->sendMessage(this, m);
- } else {
- // If the looper thread is detached from Choreographer, then refresh rate
- // changes will be handled in AChoreographer_handlePendingEvents, so we
- // need to wake up the looper thread by writing to the write-end of the
- // socket the looper is listening on.
- // Fortunately, these events are small so sending packets across the
- // socket should be atomic across processes.
- DisplayEventReceiver::Event event;
- event.header =
- DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
- PhysicalDisplayId::fromPort(0), systemTime()};
- injectEvent(event);
- }
-}
-
-void Choreographer::scheduleCallbacks() {
- const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t dueTime;
- {
- std::lock_guard<std::mutex> _l{mLock};
- // If there are no pending callbacks then don't schedule a vsync
- if (mFrameCallbacks.empty()) {
- return;
- }
- dueTime = mFrameCallbacks.top().dueTime;
- }
-
- if (dueTime <= now) {
- ALOGV("choreographer %p ~ scheduling vsync", this);
- scheduleVsync();
- return;
- }
-}
-
-void Choreographer::handleRefreshRateUpdates() {
- std::vector<RefreshRateCallback> callbacks{};
- const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
- const nsecs_t lastPeriod = mLatestVsyncPeriod;
- if (pendingPeriod > 0) {
- mLatestVsyncPeriod = pendingPeriod;
- }
- {
- std::lock_guard<std::mutex> _l{mLock};
- for (auto& cb : mRefreshRateCallbacks) {
- callbacks.push_back(cb);
- cb.firstCallbackFired = true;
- }
- }
-
- for (auto& cb : callbacks) {
- if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
- cb.callback(pendingPeriod, cb.data);
- }
- }
-}
-
-// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
-// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
-// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
- VsyncEventData vsyncEventData) {
- std::vector<FrameCallback> callbacks{};
- {
- std::lock_guard<std::mutex> _l{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
- callbacks.push_back(mFrameCallbacks.top());
- mFrameCallbacks.pop();
- }
- }
- mLastVsyncEventData = vsyncEventData;
- for (const auto& cb : callbacks) {
- if (cb.vsyncCallback != nullptr) {
- const ChoreographerFrameCallbackDataImpl frameCallbackData =
- createFrameCallbackData(timestamp);
- registerStartTime();
- mInCallback = true;
- cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
- &frameCallbackData),
- cb.data);
- mInCallback = false;
- } else if (cb.callback64 != nullptr) {
- cb.callback64(timestamp, cb.data);
- } else if (cb.callback != nullptr) {
- cb.callback(timestamp, cb.data);
- }
- }
-}
-
-void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
- to_string(displayId).c_str(), toString(connected));
-}
-
-void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
- LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
-}
-
-void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
- std::vector<FrameRateOverride>) {
- LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
-}
-
-void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
- ALOGV("choreographer %p ~ received null event.", this);
- handleRefreshRateUpdates();
-}
-
-void Choreographer::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- case MSG_HANDLE_REFRESH_RATE_UPDATES:
- handleRefreshRateUpdates();
- break;
- }
-}
-
-int64_t Choreographer::getFrameInterval() const {
- return mLastVsyncEventData.frameInterval;
-}
-
-bool Choreographer::inCallback() const {
- return mInCallback;
-}
-
-ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
- return {.frameTimeNanos = timestamp,
- .vsyncEventData = mLastVsyncEventData,
- .choreographer = this};
-}
-
-void Choreographer::registerStartTime() const {
- std::scoped_lock _l(gChoreographers.lock);
- for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
- while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
- gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
- }
- gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
- }
-}
-
-} // namespace android
using namespace android;
static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
@@ -488,27 +50,12 @@
// Glue for private C api
namespace android {
-void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
- std::lock_guard<std::mutex> _l(gChoreographers.lock);
- gChoreographers.mLastKnownVsync.store(vsyncPeriod);
- for (auto c : gChoreographers.ptrs) {
- c->scheduleLatestConfigRequest();
- }
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) {
+ Choreographer::signalRefreshRateCallbacks(vsyncPeriod);
}
void AChoreographer_initJVM(JNIEnv* env) {
- env->GetJavaVM(&gJni.jvm);
- // Now we need to find the java classes.
- jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
- gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
- gJni.displayManagerGlobal.getInstance =
- env->GetStaticMethodID(dmgClass, "getInstance",
- "()Landroid/hardware/display/DisplayManagerGlobal;");
- gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
- env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
- gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
- env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
- "()V");
+ Choreographer::initJVM(env);
}
AChoreographer* AChoreographer_routeGetInstance() {
@@ -583,13 +130,7 @@
}
int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
- std::scoped_lock _l(gChoreographers.lock);
- const auto iter = gChoreographers.startTimes.find(vsyncId);
- if (iter == gChoreographers.startTimes.end()) {
- ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
- return 0;
- }
- return iter->second;
+ return Choreographer::getStartTimeNanosForVsyncId(vsyncId);
}
} // namespace android
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 76b85d6..bf0805b 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -117,15 +117,6 @@
#define CHECK_NOT_NULL(name) \
LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
-namespace {
-
-sp<IBinder> getToken(ADisplay* display) {
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
- return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
-}
-
-} // namespace
-
namespace android {
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
@@ -136,19 +127,20 @@
}
std::vector<DisplayConfigImpl> modesPerDisplay[size];
+ ui::DisplayConnectionType displayConnectionTypes[size];
int numModes = 0;
for (int i = 0; i < size; ++i) {
- const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
-
ui::StaticDisplayInfo staticInfo;
- if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo);
+ if (const status_t status =
+ SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
status != OK) {
return status;
}
+ displayConnectionTypes[i] = staticInfo.connectionType;
ui::DynamicDisplayInfo dynamicInfo;
if (const status_t status =
- SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(ids[i].value, &dynamicInfo);
status != OK) {
return status;
}
@@ -168,8 +160,6 @@
}
}
- const std::optional<PhysicalDisplayId> internalId =
- SurfaceComposerClient::getInternalDisplayId();
ui::Dataspace defaultDataspace;
ui::PixelFormat defaultPixelFormat;
ui::Dataspace wcgDataspace;
@@ -201,8 +191,9 @@
for (size_t i = 0; i < size; ++i) {
const PhysicalDisplayId id = ids[i];
- const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
- : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+ const ADisplayType type = (displayConnectionTypes[i] == ui::DisplayConnectionType::Internal)
+ ? ADisplayType::DISPLAY_TYPE_INTERNAL
+ : ADisplayType::DISPLAY_TYPE_EXTERNAL;
const std::vector<DisplayConfigImpl>& configs = modesPerDisplay[i];
memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
@@ -259,14 +250,15 @@
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
CHECK_NOT_NULL(display);
- sp<IBinder> token = getToken(display);
ui::DynamicDisplayInfo info;
- if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ if (const auto status =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(impl->id.value, &info);
status != OK) {
return status;
}
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
for (size_t i = 0; i < impl->numConfigs; i++) {
auto* config = impl->configs + i;
if (config->id == info.activeDisplayModeId) {
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 8d8a2bc..70de33d 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -56,6 +56,7 @@
":libgui_frame_event_aidl",
"AChoreographer.cpp",
"ADisplay.cpp",
+ "Choreographer.cpp",
"surfacetexture/surface_texture.cpp",
"surfacetexture/SurfaceTexture.cpp",
"surfacetexture/ImageConsumer.cpp",
diff --git a/libs/nativedisplay/Choreographer.cpp b/libs/nativedisplay/Choreographer.cpp
new file mode 100644
index 0000000..01e9f04
--- /dev/null
+++ b/libs/nativedisplay/Choreographer.cpp
@@ -0,0 +1,390 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#include <jni.h>
+#include <nativedisplay/Choreographer.h>
+
+#undef LOG_TAG
+#define LOG_TAG "AChoreographer"
+
+namespace {
+struct {
+ // Global JVM that is provided by zygote
+ JavaVM* jvm = nullptr;
+ struct {
+ jclass clazz;
+ jmethodID getInstance;
+ jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+ jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+ } displayManagerGlobal;
+} gJni;
+
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+ if (gJni.jvm == nullptr) {
+ ALOGW("AChoreographer: No JVM provided!");
+ return nullptr;
+ }
+
+ JNIEnv* env = nullptr;
+ if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGD("Attaching thread to JVM for AChoreographer");
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+ jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+ if (attachResult != JNI_OK) {
+ ALOGE("Unable to attach thread. Error: %d", attachResult);
+ return nullptr;
+ }
+ }
+ if (env == nullptr) {
+ ALOGW("AChoreographer: No JNI env available!");
+ }
+ return env;
+}
+
+inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+} // namespace
+
+namespace android {
+
+Choreographer::Context Choreographer::gChoreographers;
+
+static thread_local Choreographer* gChoreographer;
+
+void Choreographer::initJVM(JNIEnv* env) {
+ env->GetJavaVM(&gJni.jvm);
+ // Now we need to find the java classes.
+ jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+ gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+ gJni.displayManagerGlobal.getInstance =
+ env->GetStaticMethodID(dmgClass, "getInstance",
+ "()Landroid/hardware/display/DisplayManagerGlobal;");
+ gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+ gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+ "()V");
+}
+
+Choreographer* Choreographer::getForThread() {
+ if (gChoreographer == nullptr) {
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ ALOGW("No looper prepared for thread");
+ return nullptr;
+ }
+ gChoreographer = new Choreographer(looper);
+ status_t result = gChoreographer->initialize();
+ if (result != OK) {
+ ALOGW("Failed to initialize");
+ return nullptr;
+ }
+ }
+ return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper)
+ : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
+ mLooper(looper),
+ mThreadId(std::this_thread::get_id()) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+ gChoreographers.ptrs.end(),
+ [=](Choreographer* c) { return c == this; }),
+ gChoreographers.ptrs.end());
+ // Only poke DisplayManagerGlobal to unregister if we previously registered
+ // callbacks.
+ if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ gChoreographers.registeredToDisplayManager = false;
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .unregisterNativeChoreographerForRefreshRateCallbacks);
+ env->DeleteLocalRef(dmg);
+ }
+ }
+}
+
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_vsyncCallback vsyncCallback, void* data,
+ nsecs_t delay) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ mFrameCallbacks.push(callback);
+ }
+ if (callback.dueTime <= now) {
+ if (std::this_thread::get_id() != mThreadId) {
+ if (mLooper != nullptr) {
+ Message m{MSG_SCHEDULE_VSYNC};
+ mLooper->sendMessage(this, m);
+ } else {
+ scheduleVsync();
+ }
+ } else {
+ scheduleVsync();
+ }
+ } else {
+ if (mLooper != nullptr) {
+ Message m{MSG_SCHEDULE_CALLBACKS};
+ mLooper->sendMessageDelayed(delay, this, m);
+ } else {
+ scheduleCallbacks();
+ }
+ }
+}
+
+void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (const auto& callback : mRefreshRateCallbacks) {
+ // Don't re-add callbacks.
+ if (cb == callback.callback && data == callback.data) {
+ return;
+ }
+ }
+ mRefreshRateCallbacks.emplace_back(
+ RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+ bool needsRegistration = false;
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ needsRegistration = !gChoreographers.registeredToDisplayManager;
+ }
+ if (needsRegistration) {
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping registration");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet: skipping registration");
+ return;
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .registerNativeChoreographerForRefreshRateCallbacks,
+ reinterpret_cast<int64_t>(this));
+ env->DeleteLocalRef(dmg);
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ gChoreographers.registeredToDisplayManager = true;
+ }
+ }
+ } else {
+ scheduleLatestConfigRequest();
+ }
+}
+
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+ void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+ mRefreshRateCallbacks.end(),
+ [&](const RefreshRateCallback& callback) {
+ return cb == callback.callback &&
+ data == callback.data;
+ }),
+ mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+ if (mLooper != nullptr) {
+ Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+ mLooper->sendMessage(this, m);
+ } else {
+ // If the looper thread is detached from Choreographer, then refresh rate
+ // changes will be handled in AChoreographer_handlePendingEvents, so we
+ // need to wake up the looper thread by writing to the write-end of the
+ // socket the looper is listening on.
+ // Fortunately, these events are small so sending packets across the
+ // socket should be atomic across processes.
+ DisplayEventReceiver::Event event;
+ event.header =
+ DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ PhysicalDisplayId::fromPort(0), systemTime()};
+ injectEvent(event);
+ }
+}
+
+void Choreographer::scheduleCallbacks() {
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t dueTime;
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ // If there are no pending callbacks then don't schedule a vsync
+ if (mFrameCallbacks.empty()) {
+ return;
+ }
+ dueTime = mFrameCallbacks.top().dueTime;
+ }
+
+ if (dueTime <= now) {
+ ALOGV("choreographer %p ~ scheduling vsync", this);
+ scheduleVsync();
+ return;
+ }
+}
+
+void Choreographer::handleRefreshRateUpdates() {
+ std::vector<RefreshRateCallback> callbacks{};
+ const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+ const nsecs_t lastPeriod = mLatestVsyncPeriod;
+ if (pendingPeriod > 0) {
+ mLatestVsyncPeriod = pendingPeriod;
+ }
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (auto& cb : mRefreshRateCallbacks) {
+ callbacks.push_back(cb);
+ cb.firstCallbackFired = true;
+ }
+ }
+
+ for (auto& cb : callbacks) {
+ if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+ cb.callback(pendingPeriod, cb.data);
+ }
+ }
+}
+
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+ VsyncEventData vsyncEventData) {
+ std::vector<FrameCallback> callbacks{};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+ callbacks.push_back(mFrameCallbacks.top());
+ mFrameCallbacks.pop();
+ }
+ }
+ mLastVsyncEventData = vsyncEventData;
+ for (const auto& cb : callbacks) {
+ if (cb.vsyncCallback != nullptr) {
+ const ChoreographerFrameCallbackDataImpl frameCallbackData =
+ createFrameCallbackData(timestamp);
+ registerStartTime();
+ mInCallback = true;
+ cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+ &frameCallbackData),
+ cb.data);
+ mInCallback = false;
+ } else if (cb.callback64 != nullptr) {
+ cb.callback64(timestamp, cb.data);
+ } else if (cb.callback != nullptr) {
+ cb.callback(timestamp, cb.data);
+ }
+ }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+ to_string(displayId).c_str(), toString(connected));
+}
+
+void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+ LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
+}
+
+void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
+ std::vector<FrameRateOverride>) {
+ LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
+}
+
+void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
+ ALOGV("choreographer %p ~ received null event.", this);
+ handleRefreshRateUpdates();
+}
+
+void Choreographer::handleMessage(const Message& message) {
+ switch (message.what) {
+ case MSG_SCHEDULE_CALLBACKS:
+ scheduleCallbacks();
+ break;
+ case MSG_SCHEDULE_VSYNC:
+ scheduleVsync();
+ break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
+ }
+}
+
+int64_t Choreographer::getFrameInterval() const {
+ return mLastVsyncEventData.frameInterval;
+}
+
+bool Choreographer::inCallback() const {
+ return mInCallback;
+}
+
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+ return {.frameTimeNanos = timestamp,
+ .vsyncEventData = mLastVsyncEventData,
+ .choreographer = this};
+}
+
+void Choreographer::registerStartTime() const {
+ std::scoped_lock _l(gChoreographers.lock);
+ for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
+ while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
+ gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
+ }
+ gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+}
+
+void Choreographer::signalRefreshRateCallbacks(nsecs_t vsyncPeriod) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+ for (auto c : gChoreographers.ptrs) {
+ c->scheduleLatestConfigRequest();
+ }
+}
+
+int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
+ std::scoped_lock _l(gChoreographers.lock);
+ const auto iter = gChoreographers.startTimes.find(vsyncId);
+ if (iter == gChoreographers.startTimes.end()) {
+ ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
+ return 0;
+ }
+ return iter->second;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/nativedisplay/include/nativedisplay/Choreographer.h b/libs/nativedisplay/include/nativedisplay/Choreographer.h
new file mode 100644
index 0000000..bb63f29
--- /dev/null
+++ b/libs/nativedisplay/include/nativedisplay/Choreographer.h
@@ -0,0 +1,138 @@
+/*
+ * 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 <gui/DisplayEventDispatcher.h>
+#include <private/android/choreographer.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace android {
+using gui::VsyncEventData;
+
+struct FrameCallback {
+ AChoreographer_frameCallback callback;
+ AChoreographer_frameCallback64 callback64;
+ AChoreographer_vsyncCallback vsyncCallback;
+ void* data;
+ nsecs_t dueTime;
+
+ inline bool operator<(const FrameCallback& rhs) const {
+ // Note that this is intentionally flipped because we want callbacks due sooner to be at
+ // the head of the queue
+ return dueTime > rhs.dueTime;
+ }
+};
+
+struct RefreshRateCallback {
+ AChoreographer_refreshRateCallback callback;
+ void* data;
+ bool firstCallbackFired = false;
+};
+
+class Choreographer;
+
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+ int64_t frameTimeNanos{0};
+
+ VsyncEventData vsyncEventData;
+
+ const Choreographer* choreographer;
+};
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+ struct Context {
+ std::mutex lock;
+ std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+ std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
+ bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+ std::atomic<nsecs_t> mLastKnownVsync = -1;
+ };
+ static Context gChoreographers;
+
+ explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
+ void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_vsyncCallback vsyncCallback, void* data,
+ nsecs_t delay);
+ void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+ EXCLUDES(gChoreographers.lock);
+ void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+ // Drains the queue of pending vsync periods and dispatches refresh rate
+ // updates to callbacks.
+ // The assumption is that this method is only called on a single
+ // processing thread, either by looper or by AChoreographer_handleEvents
+ void handleRefreshRateUpdates();
+ void scheduleLatestConfigRequest();
+
+ enum {
+ MSG_SCHEDULE_CALLBACKS = 0,
+ MSG_SCHEDULE_VSYNC = 1,
+ MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
+ };
+ virtual void handleMessage(const Message& message) override;
+
+ static void initJVM(JNIEnv* env);
+ static Choreographer* getForThread();
+ static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
+ static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
+ virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+ int64_t getFrameInterval() const;
+ bool inCallback() const;
+
+private:
+ Choreographer(const Choreographer&) = delete;
+
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ VsyncEventData vsyncEventData) override;
+ void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+ nsecs_t vsyncPeriod) override;
+ void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
+ void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) override;
+
+ void scheduleCallbacks();
+
+ ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+ void registerStartTime() const;
+
+ std::mutex mLock;
+ // Protected by mLock
+ std::priority_queue<FrameCallback> mFrameCallbacks;
+ std::vector<RefreshRateCallback> mRefreshRateCallbacks;
+
+ nsecs_t mLatestVsyncPeriod = -1;
+ VsyncEventData mLastVsyncEventData;
+ bool mInCallback = false;
+
+ const sp<Looper> mLooper;
+ const std::thread::id mThreadId;
+
+ // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
+ static constexpr size_t kMaxStartTimes = 250;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 180dce9..bbafbff 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -702,6 +702,14 @@
return ahardwarebuffer_format;
}
+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);
+}
+
uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
using android::hardware::graphics::common::V1_1::BufferUsage;
static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER,
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b075080..b7b2926 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -20,10 +20,15 @@
// from nativewindow/includes/system/window.h
// (not to be confused with the compatibility-only window.h from system/core/includes)
#include <system/window.h>
+#include <android/native_window_aidl.h>
#include <private/android/AHardwareBufferHelpers.h>
+#include <log/log.h>
#include <ui/GraphicBuffer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <android/binder_libbinder.h>
using namespace android;
@@ -59,6 +64,13 @@
return false;
}
}
+static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) {
+ return Surface::getIGraphicBufferProducer(window);
+}
+
+static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) {
+ return Surface::getSurfaceControlHandle(window);
+}
/**************************************************************************************************
* NDK
@@ -220,15 +232,6 @@
return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, 0,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
- ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
-}
-
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
@@ -350,6 +353,42 @@
return native_window_set_auto_prerotation(window, autoPrerotation);
}
+binder_status_t ANativeWindow_readFromParcel(
+ const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+
+ // Use a android::view::Surface to unparcel the window
+ std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
+ status_t ret = shimSurface->readFromParcel(nativeParcel);
+ if (ret != OK) {
+ ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+ sp<Surface> surface = sp<Surface>::make(
+ shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle);
+ ANativeWindow* anw = surface.get();
+ ANativeWindow_acquire(anw);
+ *outWindow = anw;
+ return STATUS_OK;
+}
+
+binder_status_t ANativeWindow_writeToParcel(
+ ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
+ int value;
+ int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+ if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+ ALOGE("Error: ANativeWindow is not backed by Surface");
+ return STATUS_BAD_VALUE;
+ }
+ // Use a android::view::Surface to parcelize the window
+ std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
+ shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window);
+ shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window);
+
+ Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return shimSurface->writeToParcel(nativeParcel);
+}
+
/**************************************************************************************************
* apex-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 3503a9e..bc0bfc5 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -74,6 +74,9 @@
override_export_include_dirs: [
"include",
],
+ export_llndk_headers: [
+ "libarect_headers",
+ ],
},
export_include_dirs: [
"include",
@@ -107,19 +110,19 @@
static_libs: [
"libarect",
"libgrallocusage",
+ "libgui_aidl_static",
],
header_libs: [
+ "libgui_headers",
+ "libarect_headers",
"libnativebase_headers",
"libnativewindow_headers",
],
// headers we include in our public headers
- export_static_lib_headers: [
- "libarect",
- ],
-
export_header_lib_headers: [
+ "libarect_headers",
"libnativebase_headers",
],
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index ddfd1d1..6d3d295 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -52,6 +52,11 @@
// 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/native_window.h b/libs/nativewindow/include/android/native_window.h
index 281ec52..be6623e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -372,11 +372,15 @@
*
* \return 0 for success, -EINVAL if the window value is invalid.
*/
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
- __INTRODUCED_IN(__ANDROID_API_U__);
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
+ __INTRODUCED_IN(__ANDROID_API_U__) {
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
#ifdef __cplusplus
-};
+}
#endif
#endif // ANDROID_NATIVE_WINDOW_H
diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h
new file mode 100644
index 0000000..a252245
--- /dev/null
+++ b/libs/nativewindow/include/android/native_window_aidl.h
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file native_window_aidl.h
+ * @brief NativeWindow NDK AIDL glue code
+ */
+
+/**
+ * @addtogroup ANativeWindow
+ *
+ * Parcelable support for ANativeWindow. Can be used with libbinder_ndk
+ *
+ * @{
+ */
+
+#ifndef ANDROID_NATIVE_WINDOW_AIDL_H
+#define ANDROID_NATIVE_WINDOW_AIDL_H
+
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Read an ANativeWindow from a AParcel. The output buffer will have an
+ * initial reference acquired and will need to be released with
+ * ANativeWindow_release.
+ *
+ * Available since API level 34.
+ *
+ * \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 ANativeWindow type
+ * STATUS_NO_MEMORY if an allocation fails
+ */
+binder_status_t ANativeWindow_readFromParcel(const AParcel* _Nonnull parcel,
+ ANativeWindow* _Nullable* _Nonnull outWindow) __INTRODUCED_IN(__ANDROID_API_U__);
+
+/**
+ * Write an ANativeWindow to an AParcel.
+ *
+ * Available since API level 34.
+ *
+ * \return STATUS_OK on success.
+ * STATUS_BAD_VALUE if either buffer or parcel is null, or if the ANativeWindow*
+ * fails to serialize (eg, internally corrupted)
+ * STATUS_NO_MEMORY if the parcel runs out of space to store the buffer & is
+ * unable to allocate more
+ * STATUS_FDS_NOT_ALLOWED if the parcel does not allow storing FDs
+ */
+binder_status_t ANativeWindow_writeToParcel(ANativeWindow* _Nonnull window,
+ AParcel* _Nonnull parcel) __INTRODUCED_IN(__ANDROID_API_U__);
+
+__END_DECLS
+
+// Only enable the AIDL glue helper if this is C++
+#ifdef __cplusplus
+
+namespace aidl::android::hardware {
+
+/**
+ * Wrapper class that enables interop with AIDL NDK generation
+ * 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 {
+public:
+ NativeWindow() noexcept {}
+ explicit NativeWindow(ANativeWindow* _Nullable window) {
+ reset(window);
+ }
+
+ explicit NativeWindow(NativeWindow&& other) noexcept {
+ mWindow = other.release(); // steal ownership from r-value
+ }
+
+ ~NativeWindow() {
+ reset();
+ }
+
+ binder_status_t readFromParcel(const AParcel* _Nonnull parcel) {
+ reset();
+ return ANativeWindow_readFromParcel(parcel, &mWindow);
+ }
+
+ binder_status_t writeToParcel(AParcel* _Nonnull parcel) const {
+ if (!mWindow) {
+ return STATUS_BAD_VALUE;
+ }
+ return ANativeWindow_writeToParcel(mWindow, parcel);
+ }
+
+ /**
+ * Destroys any currently owned ANativeWindow* and takes ownership of the given
+ * ANativeWindow*
+ *
+ * @param buffer The buffer to take ownership of
+ */
+ void reset(ANativeWindow* _Nullable window = nullptr) noexcept {
+ if (mWindow) {
+ ANativeWindow_release(mWindow);
+ mWindow = nullptr;
+ }
+ if (window != nullptr) {
+ ANativeWindow_acquire(window);
+ }
+ 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;
+ }
+
+ /**
+ * Stops managing any contained ANativeWindow*, returning it to the caller. Ownership
+ * is released.
+ * @return ANativeWindow* or null if this was empty
+ */
+ [[nodiscard]] ANativeWindow* _Nullable release() noexcept {
+ ANativeWindow* _Nullable ret = mWindow;
+ mWindow = nullptr;
+ return ret;
+ }
+private:
+ ANativeWindow* _Nullable mWindow = nullptr;
+ NativeWindow(const NativeWindow &other) = delete;
+ NativeWindow& operator=(const NativeWindow &other) = delete;
+};
+
+} // aidl::android::hardware
+ //
+namespace aidl::android::view {
+ using Surface = aidl::android::hardware::NativeWindow;
+}
+
+#endif // __cplusplus
+
+#endif // ANDROID_NATIVE_WINDOW_AIDL_H
+
+/** @} */
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 79f49e1..c7745e6 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -235,8 +235,8 @@
NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25,
NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26,
NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27,
- NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28,
- NATIVE_WINDOW_GET_HDR_SUPPORT = 29,
+ /* 28, removed: NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT */
+ /* 29, removed: NATIVE_WINDOW_GET_HDR_SUPPORT */
NATIVE_WINDOW_SET_USAGE64 = ANATIVEWINDOW_PERFORM_SET_USAGE64,
NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
@@ -988,15 +988,34 @@
outDequeueReadyTime, outReleaseTime);
}
-static inline int native_window_get_wide_color_support(
- struct ANativeWindow* window, bool* outSupport) {
- return window->perform(window, NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT,
- outSupport);
+/* deprecated. Always returns 0 and outSupport holds true. Don't call. */
+static inline int native_window_get_wide_color_support (
+ struct ANativeWindow* window __UNUSED, bool* outSupport) __deprecated;
+
+/*
+ Deprecated(b/242763577): to be removed, this method should not be used
+ Surface support should not be tied to the display
+ Return true since most displays should have this support
+*/
+static inline int native_window_get_wide_color_support (
+ struct ANativeWindow* window __UNUSED, bool* outSupport) {
+ *outSupport = true;
+ return 0;
}
-static inline int native_window_get_hdr_support(struct ANativeWindow* window,
+/* deprecated. Always returns 0 and outSupport holds true. Don't call. */
+static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED,
+ bool* outSupport) __deprecated;
+
+/*
+ Deprecated(b/242763577): to be removed, this method should not be used
+ Surface support should not be tied to the display
+ Return true since most displays should have this support
+*/
+static inline int native_window_get_hdr_support(struct ANativeWindow* window __UNUSED,
bool* outSupport) {
- return window->perform(window, NATIVE_WINDOW_GET_HDR_SUPPORT, outSupport);
+ *outSupport = true;
+ return 0;
}
static inline int native_window_get_consumer_usage(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index ce108b6..c2fd6ef 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -51,12 +51,13 @@
ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
- ANativeWindow_clearFrameRate; # introduced=UpsideDownCake
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
ANativeWindow_tryAllocateBuffers; # introduced=30
ANativeWindow_unlockAndPost;
+ ANativeWindow_readFromParcel; # introduced=UpsideDownCake
+ ANativeWindow_writeToParcel; # introduced=UpsideDownCake
local:
*;
};
@@ -69,6 +70,7 @@
android::AHardwareBuffer_convertToPixelFormat*;
android::AHardwareBuffer_convertFromGrallocUsageBits*;
android::AHardwareBuffer_convertToGrallocUsageBits*;
+ android::AHardwareBuffer_getDataSpace*;
android::AHardwareBuffer_to_GraphicBuffer*;
android::AHardwareBuffer_to_ANativeWindowBuffer*;
android::AHardwareBuffer_from_GraphicBuffer*;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 0540538..04e24ed 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -42,6 +42,7 @@
"libsync",
"libui",
"libutils",
+ "libvulkan",
],
static_libs: [
@@ -97,6 +98,7 @@
"skia/ColorSpaces.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
+ "skia/SkiaVkRenderEngine.cpp",
"skia/debug/CaptureTimer.cpp",
"skia/debug/CommonPool.cpp",
"skia/debug/SkiaCapture.cpp",
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 9d9cb6b..341c011 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -19,9 +19,11 @@
#include <cutils/properties.h>
#include <log/log.h>
#include "gl/GLESRenderEngine.h"
+#include "renderengine/ExternalTexture.h"
#include "threaded/RenderEngineThreaded.h"
#include "skia/SkiaGLRenderEngine.h"
+#include "skia/SkiaVkRenderEngine.h"
namespace android {
namespace renderengine {
@@ -36,6 +38,13 @@
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
+ case RenderEngineType::SKIA_VK:
+#ifdef RE_SKIAVK
+ ALOGD("RenderEngine with SkiaVK Backend");
+ return renderengine::skia::SkiaVkRenderEngine::create(args);
+#else
+ LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!");
+#endif
case RenderEngineType::SKIA_GL_THREADED: {
ALOGD("Threaded RenderEngine with SkiaGL Backend");
return renderengine::threaded::RenderEngineThreaded::create(
@@ -44,6 +53,17 @@
},
args.renderEngineType);
}
+ case RenderEngineType::SKIA_VK_THREADED:
+#ifdef RE_SKIAVK
+ ALOGD("Threaded RenderEngine with SkiaVK Backend");
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [args]() {
+ return android::renderengine::skia::SkiaVkRenderEngine::create(args);
+ },
+ args.renderEngineType);
+#else
+ LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!");
+#endif
case RenderEngineType::GLES:
default:
ALOGD("RenderEngine with GLES Backend");
@@ -70,10 +90,22 @@
base::unique_fd&& bufferFence) {
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
+ updateProtectedContext(layers, buffer);
drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
std::move(bufferFence));
return resultFuture;
}
+void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer) {
+ const bool needsProtectedContext =
+ (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) ||
+ std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) {
+ const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer;
+ return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ });
+ useProtectedContext(needsProtectedContext);
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 739f3fa..bd7b617 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -39,6 +39,10 @@
return "skiaglthreaded";
case RenderEngine::RenderEngineType::SKIA_GL:
return "skiagl";
+ case RenderEngine::RenderEngineType::SKIA_VK:
+ return "skiavk";
+ case RenderEngine::RenderEngineType::SKIA_VK_THREADED:
+ return "skiavkthreaded";
case RenderEngine::RenderEngineType::GLES:
case RenderEngine::RenderEngineType::THREADED:
LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
@@ -80,16 +84,26 @@
std::once_flag once;
std::call_once(once, []() {
auto surfaceComposerClient = SurfaceComposerClient::getDefault();
- auto displayToken = surfaceComposerClient->getInternalDisplayToken();
- ui::DisplayMode displayMode;
- if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
- LOG_ALWAYS_FATAL("Failed to get active display mode!");
+ auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ LOG_ALWAYS_FATAL_IF(ids.empty(), "Failed to get any display!");
+ ui::Size resolution = ui::kEmptySize;
+ // find the largest display resolution
+ for (auto id : ids) {
+ auto displayToken = surfaceComposerClient->getPhysicalDisplayToken(id);
+ ui::DisplayMode displayMode;
+ if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+ LOG_ALWAYS_FATAL("Failed to get active display mode!");
+ }
+ auto tw = displayMode.resolution.width;
+ auto th = displayMode.resolution.height;
+ LOG_ALWAYS_FATAL_IF(tw <= 0 || th <= 0, "Invalid display size!");
+ if (resolution.width * resolution.height <
+ displayMode.resolution.width * displayMode.resolution.height) {
+ resolution = displayMode.resolution;
+ }
}
- auto w = displayMode.resolution.width;
- auto h = displayMode.resolution.height;
- LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
- width = static_cast<uint32_t>(w);
- height = static_cast<uint32_t>(h);
+ width = static_cast<uint32_t>(resolution.width);
+ height = static_cast<uint32_t>(resolution.height);
});
return std::pair<uint32_t, uint32_t>(width, height);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 1ee5cba..1b34921 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -61,7 +61,7 @@
std::future<void> primeCache() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
- bool isProtected() const override { return mInProtectedContext; }
+ bool isProtected() const { return mInProtectedContext; }
bool supportsProtectedContent() const override;
void useProtectedContext(bool useProtectedContext) override;
void cleanupPostRender() override;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 5ff9240..f7f2d54 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -601,7 +601,7 @@
}
if (needs.hasTextureCoords()) {
- fs << "varying vec2 outTexCoords;";
+ fs << "varying highp vec2 outTexCoords;";
}
if (needs.hasRoundedCorners()) {
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 25fe9f2..8d7c13c 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -23,17 +23,24 @@
#include <math/mat4.h>
#include <renderengine/PrintMatrix.h>
#include <renderengine/BorderRenderInfo.h>
+#include <ui/DisplayId.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
+#include <optional>
+
namespace android {
namespace renderengine {
// DisplaySettings contains the settings that are applicable when drawing all
// layers for a given display.
struct DisplaySettings {
+ // A string containing the name of the display, along with its id, if it has
+ // one.
+ std::string namePlusId;
+
// Rectangle describing the physical display. We will project from the
// logical clip onto this rectangle.
Rect physicalDisplay = Rect::INVALID_RECT;
@@ -85,8 +92,8 @@
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
- return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
- lhs.maxLuminance == rhs.maxLuminance &&
+ return lhs.namePlusId == rhs.namePlusId && lhs.physicalDisplay == rhs.physicalDisplay &&
+ lhs.clip == rhs.clip && lhs.maxLuminance == rhs.maxLuminance &&
lhs.currentLuminanceNits == rhs.currentLuminanceNits &&
lhs.outputDataspace == rhs.outputDataspace &&
lhs.colorTransform == rhs.colorTransform &&
@@ -121,6 +128,7 @@
static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
*os << "DisplaySettings {";
+ *os << "\n .display = " << settings.namePlusId;
*os << "\n .physicalDisplay = ";
PrintTo(settings.physicalDisplay, os);
*os << "\n .clip = ";
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 199392c..39621cd 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -99,6 +99,8 @@
THREADED = 2,
SKIA_GL = 3,
SKIA_GL_THREADED = 4,
+ SKIA_VK = 5,
+ SKIA_VK_THREADED = 6,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
@@ -126,12 +128,8 @@
// ----- BEGIN NEW INTERFACE -----
// queries that are required to be thread safe
- virtual bool isProtected() const = 0;
virtual bool supportsProtectedContent() const = 0;
- // Attempt to switch RenderEngine into and out of protectedContext mode
- virtual void useProtectedContext(bool useProtectedContext) = 0;
-
// Notify RenderEngine of changes to the dimensions of the active display
// so that it can configure its internal caches accordingly.
virtual void onActiveDisplaySizeChanged(ui::Size size) = 0;
@@ -174,9 +172,16 @@
virtual void cleanupPostRender() = 0;
virtual void cleanFramebufferCache() = 0;
- // Returns the priority this context was actually created with. Note: this may not be
- // the same as specified at context creation time, due to implementation limits on the
- // number of contexts that can be created at a specific priority level in the system.
+
+ // Returns the priority this context was actually created with. Note: this
+ // may not be the same as specified at context creation time, due to
+ // implementation limits on the number of contexts that can be created at a
+ // specific priority level in the system.
+ //
+ // This should return a valid EGL context priority enum as described by
+ // https://registry.khronos.org/EGL/extensions/IMG/EGL_IMG_context_priority.txt
+ // or
+ // https://registry.khronos.org/EGL/extensions/NV/EGL_NV_context_priority_realtime.txt
virtual int getContextPriority() = 0;
// Returns true if blur was requested in the RenderEngineCreationArgs and the implementation
@@ -238,6 +243,13 @@
friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
const RenderEngineType mRenderEngineType;
+ // Update protectedContext mode depending on whether or not any layer has a protected buffer.
+ void updateProtectedContext(const std::vector<LayerSettings>&,
+ const std::shared_ptr<ExternalTexture>&);
+
+ // Attempt to switch RenderEngine into and out of protectedContext mode
+ virtual void useProtectedContext(bool useProtectedContext) = 0;
+
virtual void drawLayersInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 347b8b7..ff598e7 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -193,9 +193,9 @@
}
// initialize the renderer while GL is current
- std::unique_ptr<SkiaGLRenderEngine> engine =
- std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
- protectedPlaceholder);
+ std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt,
+ placeholder, protectedContext,
+ protectedPlaceholder));
engine->ensureGrContextsCreated();
ALOGI("OpenGL ES informations:");
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 4a37ffe..af33110 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -52,9 +52,6 @@
class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
public:
static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
- EGLSurface placeholder, EGLContext protectedContext,
- EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override;
int getContextPriority() override;
@@ -70,6 +67,9 @@
void appendBackendSpecificInfoToDump(std::string& result) override;
private:
+ SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
+ EGLSurface placeholder, EGLContext protectedContext,
+ EGLSurface protectedPlaceholder);
bool waitGpuFence(base::borrowed_fd fenceFd);
base::unique_fd flush();
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index b9aa5ac..413811e 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -388,9 +388,11 @@
void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
- // Only run this if RE is running on its own thread. This way the access to GL
- // operations is guaranteed to be happening on the same thread.
- if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
+ // Only run this if RE is running on its own thread. This
+ // way the access to GL operations is guaranteed to be happening on the
+ // same thread.
+ if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED &&
+ mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) {
return;
}
// We currently don't attempt to map a buffer if the buffer contains protected content
@@ -636,7 +638,7 @@
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
base::unique_fd&& bufferFence) {
- ATRACE_NAME("SkiaGL::drawLayersInternal");
+ ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
std::lock_guard<std::mutex> lock(mRenderingMutex);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index e7c5b8f..1973c7d 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -68,7 +68,6 @@
std::future<void> primeCache() override final;
void cleanupPostRender() override final;
void cleanFramebufferCache() override final{ }
- bool isProtected() const override final{ return mInProtectedContext; }
bool supportsBackgroundBlur() override final {
return mBlurFilter != nullptr;
}
@@ -102,6 +101,8 @@
size_t getMaxViewportDims() const override final;
GrDirectContext* getActiveGrContext();
+ bool isProtected() const { return mInProtectedContext; }
+
// Implements PersistentCache as a way to monitor what SkSL shaders Skia has
// cached.
class SkSLCacheMonitor : public GrContextOptions::PersistentCache {
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
new file mode 100644
index 0000000..2b8495c
--- /dev/null
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -0,0 +1,680 @@
+/*
+ * 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.
+ */
+
+// Allow the SkiaVkRenderEngine class to not be compiled, to save space
+// NOTE: In order to build this class, define `RE_SKIAVK` in a build file.
+#ifdef RE_SKIAVK
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "SkiaVkRenderEngine.h"
+
+#include <GrBackendSemaphore.h>
+#include <GrContextOptions.h>
+#include <vk/GrVkExtensions.h>
+#include <vk/GrVkTypes.h>
+
+#include <android-base/stringprintf.h>
+#include <gui/TraceUtils.h>
+#include <sync/sync.h>
+#include <utils/Trace.h>
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <vulkan/vulkan.h>
+#include "log/log_main.h"
+
+namespace android {
+namespace renderengine {
+
+struct VulkanFuncs {
+ PFN_vkCreateSemaphore vkCreateSemaphore = nullptr;
+ PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr;
+ PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr;
+ PFN_vkDestroySemaphore vkDestroySemaphore = nullptr;
+
+ PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr;
+ PFN_vkDestroyDevice vkDestroyDevice = nullptr;
+ PFN_vkDestroyInstance vkDestroyInstance = nullptr;
+};
+
+struct VulkanInterface {
+ bool initialized = false;
+ VkInstance instance;
+ VkPhysicalDevice physicalDevice;
+ VkDevice device;
+ VkQueue queue;
+ int queueIndex;
+ uint32_t apiVersion;
+ GrVkExtensions grExtensions;
+ VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr;
+ VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr;
+ VkPhysicalDeviceProtectedMemoryProperties* protectedMemoryFeatures = nullptr;
+ GrVkGetProc grGetProc;
+ bool isProtected;
+ bool isRealtimePriority;
+
+ VulkanFuncs funcs;
+
+ std::vector<std::string> instanceExtensionNames;
+ std::vector<std::string> deviceExtensionNames;
+
+ GrVkBackendContext getBackendContext() {
+ GrVkBackendContext backendContext;
+ backendContext.fInstance = instance;
+ backendContext.fPhysicalDevice = physicalDevice;
+ backendContext.fDevice = device;
+ backendContext.fQueue = queue;
+ backendContext.fGraphicsQueueIndex = queueIndex;
+ backendContext.fMaxAPIVersion = apiVersion;
+ backendContext.fVkExtensions = &grExtensions;
+ backendContext.fDeviceFeatures2 = physicalDeviceFeatures2;
+ backendContext.fGetProc = grGetProc;
+ backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo;
+ return backendContext;
+ };
+
+ VkSemaphore createExportableSemaphore() {
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+
+ VkSemaphore semaphore;
+ VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("%s: failed to create semaphore. err %d\n", __func__, err);
+ return VK_NULL_HANDLE;
+ }
+
+ return semaphore;
+ }
+
+ // syncFd cannot be <= 0
+ VkSemaphore importSemaphoreFromSyncFd(int syncFd) {
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+
+ VkSemaphore semaphore;
+ VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("%s: failed to create import semaphore", __func__);
+ return VK_NULL_HANDLE;
+ }
+
+ VkImportSemaphoreFdInfoKHR importInfo;
+ importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+ importInfo.pNext = nullptr;
+ importInfo.semaphore = semaphore;
+ importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+ importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ importInfo.fd = syncFd;
+
+ err = funcs.vkImportSemaphoreFdKHR(device, &importInfo);
+ if (VK_SUCCESS != err) {
+ funcs.vkDestroySemaphore(device, semaphore, nullptr);
+ ALOGE("%s: failed to import semaphore", __func__);
+ return VK_NULL_HANDLE;
+ }
+
+ return semaphore;
+ }
+
+ int exportSemaphoreSyncFd(VkSemaphore semaphore) {
+ int res;
+
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res);
+ if (VK_SUCCESS != err) {
+ ALOGE("%s: failed to export semaphore, err: %d", __func__, err);
+ return -1;
+ }
+ return res;
+ }
+
+ void destroySemaphore(VkSemaphore semaphore) {
+ funcs.vkDestroySemaphore(device, semaphore, nullptr);
+ }
+};
+
+static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+};
+
+#define BAIL(fmt, ...) \
+ { \
+ ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \
+ return interface; \
+ }
+
+#define CHECK_NONNULL(expr) \
+ if ((expr) == nullptr) { \
+ BAIL("[%s] null", #expr); \
+ }
+
+#define VK_CHECK(expr) \
+ if ((expr) != VK_SUCCESS) { \
+ BAIL("[%s] failed. err = %d", #expr, expr); \
+ return interface; \
+ }
+
+#define VK_GET_PROC(F) \
+ PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \
+ CHECK_NONNULL(vk##F)
+#define VK_GET_INST_PROC(instance, F) \
+ PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \
+ CHECK_NONNULL(vk##F)
+#define VK_GET_DEV_PROC(device, F) \
+ PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \
+ CHECK_NONNULL(vk##F)
+
+VulkanInterface initVulkanInterface(bool protectedContent = false) {
+ VulkanInterface interface;
+
+ VK_GET_PROC(EnumerateInstanceVersion);
+ uint32_t instanceVersion;
+ VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion));
+
+ if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ return interface;
+ }
+
+ const VkApplicationInfo appInfo = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0,
+ VK_MAKE_VERSION(1, 1, 0),
+ };
+
+ VK_GET_PROC(EnumerateInstanceExtensionProperties);
+
+ uint32_t extensionCount = 0;
+ VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr));
+ std::vector<VkExtensionProperties> instanceExtensions(extensionCount);
+ VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
+ instanceExtensions.data()));
+ std::vector<const char*> enabledInstanceExtensionNames;
+ enabledInstanceExtensionNames.reserve(instanceExtensions.size());
+ interface.instanceExtensionNames.reserve(instanceExtensions.size());
+ for (const auto& instExt : instanceExtensions) {
+ enabledInstanceExtensionNames.push_back(instExt.extensionName);
+ interface.instanceExtensionNames.push_back(instExt.extensionName);
+ }
+
+ const VkInstanceCreateInfo instanceCreateInfo = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ nullptr,
+ 0,
+ &appInfo,
+ 0,
+ nullptr,
+ (uint32_t)enabledInstanceExtensionNames.size(),
+ enabledInstanceExtensionNames.data(),
+ };
+
+ VK_GET_PROC(CreateInstance);
+ VkInstance instance;
+ VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
+
+ VK_GET_INST_PROC(instance, DestroyInstance);
+ interface.funcs.vkDestroyInstance = vkDestroyInstance;
+ VK_GET_INST_PROC(instance, EnumeratePhysicalDevices);
+ VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2);
+ VK_GET_INST_PROC(instance, CreateDevice);
+
+ uint32_t physdevCount;
+ VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr));
+ if (physdevCount == 0) {
+ BAIL("Could not find any physical devices");
+ }
+
+ physdevCount = 1;
+ VkPhysicalDevice physicalDevice;
+ VkResult enumeratePhysDevsErr =
+ vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice);
+ if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) {
+ BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d",
+ enumeratePhysDevsErr);
+ }
+
+ VkPhysicalDeviceProperties2 physDevProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ 0,
+ {},
+ };
+ VkPhysicalDeviceProtectedMemoryProperties protMemProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
+ 0,
+ {},
+ };
+
+ if (protectedContent) {
+ physDevProps.pNext = &protMemProps;
+ }
+
+ vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps);
+ if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ BAIL("Could not find a Vulkan 1.1+ physical device");
+ }
+
+ // Check for syncfd support. Bail if we cannot both import and export them.
+ VkPhysicalDeviceExternalSemaphoreInfo semInfo = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
+ nullptr,
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ };
+ VkExternalSemaphoreProperties semProps = {
+ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0,
+ };
+ vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps);
+
+ bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes &
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
+ (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
+ (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) &&
+ (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+
+ if (!sufficientSemaphoreSyncFdSupport) {
+ BAIL("Vulkan device does not support sufficient external semaphore sync fd features. "
+ "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
+ "compatibleHandleTypes 0x%x (needed 0x%x) "
+ "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
+ semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.externalSemaphoreFeatures,
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+ VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+ } else {
+ ALOGD("Vulkan device supports sufficient external semaphore sync fd features. "
+ "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
+ "compatibleHandleTypes 0x%x (needed 0x%x) "
+ "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
+ semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.externalSemaphoreFeatures,
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+ VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+ }
+
+ uint32_t queueCount;
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, nullptr);
+ if (queueCount == 0) {
+ BAIL("Could not find queues for physical device");
+ }
+
+ std::vector<VkQueueFamilyProperties> queueProps(queueCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
+
+ int graphicsQueueIndex = -1;
+ for (uint32_t i = 0; i < queueCount; ++i) {
+ if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ graphicsQueueIndex = i;
+ break;
+ }
+ }
+
+ if (graphicsQueueIndex == -1) {
+ BAIL("Could not find a graphics queue family");
+ }
+
+ uint32_t deviceExtensionCount;
+ VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
+ nullptr));
+ std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount);
+ VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
+ deviceExtensions.data()));
+
+ std::vector<const char*> enabledDeviceExtensionNames;
+ enabledDeviceExtensionNames.reserve(deviceExtensions.size());
+ interface.deviceExtensionNames.reserve(deviceExtensions.size());
+ for (const auto& devExt : deviceExtensions) {
+ enabledDeviceExtensionNames.push_back(devExt.extensionName);
+ interface.deviceExtensionNames.push_back(devExt.extensionName);
+ }
+
+ interface.grExtensions.init(sGetProc, instance, physicalDevice,
+ enabledInstanceExtensionNames.size(),
+ enabledInstanceExtensionNames.data(),
+ enabledDeviceExtensionNames.size(),
+ enabledDeviceExtensionNames.data());
+
+ if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ BAIL("Vulkan driver doesn't support external semaphore fd");
+ }
+
+ interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2;
+ interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ interface.physicalDeviceFeatures2->pNext = nullptr;
+
+ interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures;
+ interface.samplerYcbcrConversionFeatures->sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ interface.samplerYcbcrConversionFeatures->pNext = nullptr;
+
+ interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures;
+ void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext;
+
+ if (protectedContent) {
+ interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryProperties;
+ interface.protectedMemoryFeatures->sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+ interface.protectedMemoryFeatures->pNext = nullptr;
+ *tailPnext = interface.protectedMemoryFeatures;
+ tailPnext = &interface.protectedMemoryFeatures->pNext;
+ }
+
+ vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2);
+ // Looks like this would slow things down and we can't depend on it on all platforms
+ interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE;
+
+ float queuePriorities[1] = {0.0f};
+ void* queueNextPtr = nullptr;
+
+ VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
+ nullptr,
+ // If queue priority is supported, RE should always have realtime priority.
+ VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT,
+ };
+
+ if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
+ queueNextPtr = &queuePriorityCreateInfo;
+ interface.isRealtimePriority = true;
+ }
+
+ VkDeviceQueueCreateFlags deviceQueueCreateFlags =
+ (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0);
+
+ const VkDeviceQueueCreateInfo queueInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ queueNextPtr,
+ deviceQueueCreateFlags,
+ (uint32_t)graphicsQueueIndex,
+ 1,
+ queuePriorities,
+ };
+
+ const VkDeviceCreateInfo deviceInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ interface.physicalDeviceFeatures2,
+ 0,
+ 1,
+ &queueInfo,
+ 0,
+ nullptr,
+ (uint32_t)enabledDeviceExtensionNames.size(),
+ enabledDeviceExtensionNames.data(),
+ nullptr,
+ };
+
+ ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent);
+ VkDevice device;
+ VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device));
+ ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
+
+ VkQueue graphicsQueue;
+ VK_GET_DEV_PROC(device, GetDeviceQueue);
+ vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue);
+
+ VK_GET_DEV_PROC(device, DeviceWaitIdle);
+ VK_GET_DEV_PROC(device, DestroyDevice);
+ interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle;
+ interface.funcs.vkDestroyDevice = vkDestroyDevice;
+
+ VK_GET_DEV_PROC(device, CreateSemaphore);
+ VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR);
+ VK_GET_DEV_PROC(device, GetSemaphoreFdKHR);
+ VK_GET_DEV_PROC(device, DestroySemaphore);
+ interface.funcs.vkCreateSemaphore = vkCreateSemaphore;
+ interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR;
+ interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR;
+ interface.funcs.vkDestroySemaphore = vkDestroySemaphore;
+
+ // At this point, everything's succeeded and we can continue
+ interface.initialized = true;
+ interface.instance = instance;
+ interface.physicalDevice = physicalDevice;
+ interface.device = device;
+ interface.queue = graphicsQueue;
+ interface.queueIndex = graphicsQueueIndex;
+ interface.apiVersion = physDevProps.properties.apiVersion;
+ // grExtensions already constructed
+ // feature pointers already constructed
+ interface.grGetProc = sGetProc;
+ interface.isProtected = protectedContent;
+ // funcs already initialized
+
+ ALOGD("%s: Success init Vulkan interface", __func__);
+ return interface;
+}
+
+void teardownVulkanInterface(VulkanInterface* interface) {
+ interface->initialized = false;
+
+ if (interface->device != VK_NULL_HANDLE) {
+ interface->funcs.vkDeviceWaitIdle(interface->device);
+ interface->funcs.vkDestroyDevice(interface->device, nullptr);
+ interface->device = VK_NULL_HANDLE;
+ }
+ if (interface->instance != VK_NULL_HANDLE) {
+ interface->funcs.vkDestroyInstance(interface->instance, nullptr);
+ interface->instance = VK_NULL_HANDLE;
+ }
+
+ if (interface->protectedMemoryFeatures) {
+ delete interface->protectedMemoryFeatures;
+ }
+
+ if (interface->samplerYcbcrConversionFeatures) {
+ delete interface->samplerYcbcrConversionFeatures;
+ }
+
+ if (interface->physicalDeviceFeatures2) {
+ delete interface->physicalDeviceFeatures2;
+ }
+
+ interface->samplerYcbcrConversionFeatures = nullptr;
+ interface->physicalDeviceFeatures2 = nullptr;
+ interface->protectedMemoryFeatures = nullptr;
+}
+
+static VulkanInterface sVulkanInterface;
+static VulkanInterface sProtectedContentVulkanInterface;
+
+static void sSetupVulkanInterface() {
+ if (!sVulkanInterface.initialized) {
+ sVulkanInterface = initVulkanInterface(false /* no protected content */);
+ // We will have to abort if non-protected VkDevice creation fails (then nothing works).
+ LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized,
+ "Could not initialize Vulkan RenderEngine!");
+ }
+ if (!sProtectedContentVulkanInterface.initialized) {
+ sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */);
+ if (!sProtectedContentVulkanInterface.initialized) {
+ ALOGE("Could not initialize protected content Vulkan RenderEngine.");
+ }
+ }
+}
+
+namespace skia {
+
+using base::StringAppendF;
+
+bool SkiaVkRenderEngine::canSupportSkiaVkRenderEngine() {
+ VulkanInterface temp = initVulkanInterface(false /* no protected content */);
+ ALOGD("SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(): initialized == %s.",
+ temp.initialized ? "true" : "false");
+ return temp.initialized;
+}
+
+std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create(
+ const RenderEngineCreationArgs& args) {
+ std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args));
+ engine->ensureGrContextsCreated();
+
+ if (sVulkanInterface.initialized) {
+ ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__);
+ return engine;
+ } else {
+ ALOGD("SkiaVkRenderEngine::%s: could not create SkiaVkRenderEngine. "
+ "Likely insufficient Vulkan support",
+ __func__);
+ return {};
+ }
+}
+
+SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
+ : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
+ args.useColorManagement, args.supportsBackgroundBlur) {}
+
+SkiaVkRenderEngine::~SkiaVkRenderEngine() {
+ finishRenderingAndAbandonContext();
+}
+
+SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts(
+ const GrContextOptions& options) {
+ sSetupVulkanInterface();
+
+ SkiaRenderEngine::Contexts contexts;
+ contexts.first = GrDirectContext::MakeVulkan(sVulkanInterface.getBackendContext(), options);
+ if (supportsProtectedContentImpl()) {
+ contexts.second =
+ GrDirectContext::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(),
+ options);
+ }
+
+ return contexts;
+}
+
+bool SkiaVkRenderEngine::supportsProtectedContentImpl() const {
+ return sProtectedContentVulkanInterface.initialized;
+}
+
+bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) {
+ return true;
+}
+
+static void delete_semaphore(void* _semaphore) {
+ VkSemaphore semaphore = (VkSemaphore)_semaphore;
+ sVulkanInterface.destroySemaphore(semaphore);
+}
+
+static void delete_semaphore_protected(void* _semaphore) {
+ VkSemaphore semaphore = (VkSemaphore)_semaphore;
+ sProtectedContentVulkanInterface.destroySemaphore(semaphore);
+}
+
+static VulkanInterface& getVulkanInterface(bool protectedContext) {
+ if (protectedContext) {
+ return sProtectedContentVulkanInterface;
+ }
+ return sVulkanInterface;
+}
+
+void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) {
+ if (fenceFd.get() < 0) return;
+
+ int dupedFd = dup(fenceFd.get());
+ if (dupedFd < 0) {
+ ALOGE("failed to create duplicate fence fd: %d", dupedFd);
+ sync_wait(fenceFd.get(), -1);
+ return;
+ }
+
+ base::unique_fd fenceDup(dupedFd);
+ VkSemaphore waitSemaphore =
+ getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
+ GrBackendSemaphore beSemaphore;
+ beSemaphore.initVulkan(waitSemaphore);
+ grContext->wait(1, &beSemaphore, true /* delete after wait */);
+}
+
+base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
+ VkSemaphore signalSemaphore = getVulkanInterface(isProtected()).createExportableSemaphore();
+ GrBackendSemaphore beSignalSemaphore;
+ beSignalSemaphore.initVulkan(signalSemaphore);
+ GrFlushInfo flushInfo;
+ flushInfo.fNumSemaphores = 1;
+ flushInfo.fSignalSemaphores = &beSignalSemaphore;
+ flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore;
+ flushInfo.fFinishedContext = (void*)signalSemaphore;
+ GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
+ grContext->submit(false /* no cpu sync */);
+ int drawFenceFd = -1;
+ if (GrSemaphoresSubmitted::kYes == submitted) {
+ drawFenceFd = getVulkanInterface(isProtected()).exportSemaphoreSyncFd(signalSemaphore);
+ }
+ base::unique_fd res(drawFenceFd);
+ return res;
+}
+
+int SkiaVkRenderEngine::getContextPriority() {
+ // EGL_CONTEXT_PRIORITY_REALTIME_NV
+ constexpr int kRealtimePriority = 0x3357;
+ if (getVulkanInterface(isProtected()).isRealtimePriority) {
+ return kRealtimePriority;
+ } else {
+ return 0;
+ }
+}
+
+void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
+ StringAppendF(&result, "\n ------------RE Vulkan----------\n");
+ StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized);
+ StringAppendF(&result, "\n Vulkan protected device initialized: %d\n",
+ sProtectedContentVulkanInterface.initialized);
+
+ if (!sVulkanInterface.initialized) {
+ return;
+ }
+
+ StringAppendF(&result, "\n Instance extensions:\n");
+ for (const auto& name : sVulkanInterface.instanceExtensionNames) {
+ StringAppendF(&result, "\n %s\n", name.c_str());
+ }
+
+ StringAppendF(&result, "\n Device extensions:\n");
+ for (const auto& name : sVulkanInterface.deviceExtensionNames) {
+ StringAppendF(&result, "\n %s\n", name.c_str());
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+#endif // RE_SKIAVK
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
new file mode 100644
index 0000000..1e42b80
--- /dev/null
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_SKIAVKRENDERENGINE_H_
+#define SF_SKIAVKRENDERENGINE_H_
+
+// Allow the SkiaVkRenderEngine class to not be compiled, to save space
+// NOTE: In order to build this class, define `RE_SKIAVK` in a build file.
+#ifdef RE_SKIAVK
+
+#include <vk/GrVkBackendContext.h>
+
+#include "SkiaRenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class SkiaVkRenderEngine : public SkiaRenderEngine {
+public:
+ // Returns false if Vulkan implementation can't support SkiaVkRenderEngine.
+ static bool canSupportSkiaVkRenderEngine();
+ static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args);
+ ~SkiaVkRenderEngine() override;
+
+ int getContextPriority() override;
+
+protected:
+ // Implementations of abstract SkiaRenderEngine functions specific to
+ // rendering backend
+ virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options);
+ bool supportsProtectedContentImpl() const override;
+ bool useProtectedContextImpl(GrProtected isProtected) override;
+ void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override;
+ base::unique_fd flushAndSubmit(GrDirectContext* context) override;
+ void appendBackendSpecificInfoToDump(std::string& result) override;
+
+private:
+ SkiaVkRenderEngine(const RenderEngineCreationArgs& args);
+ base::unique_fd flush();
+
+ GrVkBackendContext mBackendContext;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif // RE_SKIAVK
+#endif // SF_SKIAVKRENDERENGINE_H_
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 777d02f..7db95a7 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -38,6 +38,7 @@
#include <fstream>
#include "../skia/SkiaGLRenderEngine.h"
+#include "../skia/SkiaVkRenderEngine.h"
#include "../threaded/RenderEngineThreaded.h"
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
@@ -107,9 +108,53 @@
virtual std::string name() = 0;
virtual renderengine::RenderEngine::RenderEngineType type() = 0;
virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
+ virtual bool typeSupported() = 0;
virtual bool useColorManagement() const = 0;
};
+#ifdef RE_SKIAVK
+class SkiaVkRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "SkiaVkRenderEngineFactory"; }
+
+ renderengine::RenderEngine::RenderEngineType type() {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_VK;
+ }
+
+ std::unique_ptr<renderengine::RenderEngine> createRenderEngine() override {
+ std::unique_ptr<renderengine::RenderEngine> re = createSkiaVkRenderEngine();
+ return re;
+ }
+
+ std::unique_ptr<renderengine::skia::SkiaVkRenderEngine> createSkiaVkRenderEngine() {
+ renderengine::RenderEngineCreationArgs reCreationArgs =
+ renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setUseColorManagerment(false)
+ .setEnableProtectedContext(false)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .setRenderEngineType(type())
+ .setUseColorManagerment(useColorManagement())
+ .build();
+ return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs);
+ }
+
+ bool typeSupported() override {
+ return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine();
+ }
+ bool useColorManagement() const override { return false; }
+ void skip() { GTEST_SKIP(); }
+};
+
+class SkiaVkCMRenderEngineFactory : public SkiaVkRenderEngineFactory {
+public:
+ bool useColorManagement() const override { return true; }
+};
+#endif // RE_SKIAVK
+
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -133,6 +178,7 @@
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
+ bool typeSupported() override { return true; }
bool useColorManagement() const override { return false; }
};
@@ -159,6 +205,7 @@
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
+ bool typeSupported() override { return true; }
bool useColorManagement() const override { return true; }
};
@@ -1513,16 +1560,30 @@
expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
}
+#ifdef RE_SKIAVK
+INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
+ testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
+ std::make_shared<SkiaGLESCMRenderEngineFactory>(),
+ std::make_shared<SkiaVkRenderEngineFactory>(),
+ std::make_shared<SkiaVkCMRenderEngineFactory>()));
+#else // RE_SKIAVK
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
std::make_shared<SkiaGLESCMRenderEngineFactory>()));
+#endif // RE_SKIAVK
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
drawEmptyLayers();
}
TEST_P(RenderEngineTest, drawLayers_fillRedBufferAndEmptyBuffer) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1547,6 +1608,9 @@
}
TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -1578,6 +1642,9 @@
}
TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -1597,56 +1664,89 @@
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillRedBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillGreenBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBlueBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillRedTransparentBuffer<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferPhysicalOffset<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate0<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate90<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate180<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate270<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferLayerTransform<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferColorTransform<ColorSourceVariant>();
}
@@ -1654,7 +1754,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
GTEST_SKIP();
}
@@ -1665,7 +1765,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
GTEST_SKIP();
}
@@ -1674,81 +1774,129 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferWithRoundedCorners<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferAndBlurBackground<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillSmallLayerAndBlurBackground<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
overlayCorners<ColorSourceVariant>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
@@ -1756,7 +1904,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
GTEST_SKIP();
}
@@ -1767,7 +1915,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
GTEST_SKIP();
}
@@ -1776,81 +1924,129 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
@@ -1858,7 +2054,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
GTEST_SKIP();
}
@@ -1869,7 +2065,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
GTEST_SKIP();
}
@@ -1878,46 +2074,73 @@
}
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_P(RenderEngineTest, drawLayers_fillBufferTextureTransform) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferTextureTransform();
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferWithPremultiplyAlpha();
}
TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
fillBufferWithoutPremultiplyAlpha();
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
@@ -1934,6 +2157,9 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
@@ -1955,6 +2181,9 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
@@ -1977,6 +2206,9 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
@@ -2000,6 +2232,9 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
@@ -2024,6 +2259,9 @@
}
TEST_P(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ubyte4 casterColor(255, 0, 0, 255);
@@ -2051,6 +2289,9 @@
}
TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -2081,12 +2322,20 @@
fenceTwo->waitForever(LOG_TAG);
// Only cleanup the first time.
- EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
- mRE->cleanupPostRender();
- EXPECT_TRUE(mRE->canSkipPostRenderCleanup());
+ if (mRE->canSkipPostRenderCleanup()) {
+ // Skia's Vk backend may keep the texture alive beyond drawLayersInternal, so
+ // it never gets added to the cleanup list. In those cases, we can skip.
+ EXPECT_TRUE(GetParam()->type() == renderengine::RenderEngine::RenderEngineType::SKIA_VK);
+ } else {
+ mRE->cleanupPostRender();
+ EXPECT_TRUE(mRE->canSkipPostRenderCleanup());
+ }
}
TEST_P(RenderEngineTest, testRoundedCornersCrop) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -2137,6 +2386,9 @@
}
TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -2182,6 +2434,9 @@
}
TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
renderengine::DisplaySettings settings;
@@ -2259,6 +2514,9 @@
}
TEST_P(RenderEngineTest, testClear) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto rect = fullscreenRect();
@@ -2288,6 +2546,9 @@
}
TEST_P(RenderEngineTest, testDisableBlendingBuffer) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto rect = Rect(0, 0, 1, 1);
@@ -2385,6 +2646,9 @@
}
TEST_P(RenderEngineTest, testDimming) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -2457,6 +2721,9 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 |
@@ -2532,6 +2799,9 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 |
@@ -2592,6 +2862,9 @@
}
TEST_P(RenderEngineTest, testDimming_inGammaSpace_withDisplayColorTransform_deviceHandles) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const ui::Dataspace dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_BT709 |
@@ -2653,6 +2926,9 @@
}
TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto displayRect = Rect(2, 1);
@@ -2704,6 +2980,9 @@
}
TEST_P(RenderEngineTest, test_isOpaque) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto rect = Rect(0, 0, 1, 1);
@@ -2755,7 +3034,7 @@
}
TEST_P(RenderEngineTest, test_tonemapPQMatches) {
- if (!GetParam()->useColorManagement()) {
+ if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) {
GTEST_SKIP();
}
@@ -2772,7 +3051,7 @@
}
TEST_P(RenderEngineTest, test_tonemapHLGMatches) {
- if (!GetParam()->useColorManagement()) {
+ if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) {
GTEST_SKIP();
}
@@ -2789,6 +3068,9 @@
}
TEST_P(RenderEngineTest, r8_behaves_as_mask) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto r8Buffer = allocateR8Buffer(2, 1);
@@ -2846,6 +3128,9 @@
}
TEST_P(RenderEngineTest, r8_respects_color_transform) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto r8Buffer = allocateR8Buffer(2, 1);
@@ -2908,6 +3193,9 @@
}
TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
const auto r8Buffer = allocateR8Buffer(2, 1);
@@ -2973,6 +3261,9 @@
}
TEST_P(RenderEngineTest, primeShaderCache) {
+ if (!GetParam()->typeSupported()) {
+ GTEST_SKIP();
+ }
initializeRenderEngine();
auto fut = mRE->primeCache();
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 1a96289..fe3a16d 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -17,8 +17,10 @@
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <hardware/gralloc.h>
#include <renderengine/impl/ExternalTexture.h>
#include <renderengine/mock/RenderEngine.h>
+#include <ui/PixelFormat.h>
#include "../threaded/RenderEngineThreaded.h"
namespace android {
@@ -95,18 +97,6 @@
ASSERT_EQ(dims, result);
}
-TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
- EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
- status_t result = mThreadedRE->isProtected();
- ASSERT_EQ(false, result);
-}
-
-TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
- EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
- size_t result = mThreadedRE->isProtected();
- ASSERT_EQ(true, result);
-}
-
TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
status_t result = mThreadedRE->supportsProtectedContent();
@@ -119,28 +109,6 @@
ASSERT_EQ(true, result);
}
-TEST_F(RenderEngineThreadedTest, useProtectedContext) {
- EXPECT_CALL(*mRenderEngine, useProtectedContext(true));
- auto& ipExpect = EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
- EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
- EXPECT_CALL(*mRenderEngine, isProtected()).After(ipExpect).WillOnce(Return(true));
-
- mThreadedRE->useProtectedContext(true);
- ASSERT_EQ(true, mThreadedRE->isProtected());
-
- // call ANY synchronous function to ensure that useProtectedContext has completed.
- mThreadedRE->getContextPriority();
- ASSERT_EQ(true, mThreadedRE->isProtected());
-}
-
-TEST_F(RenderEngineThreadedTest, useProtectedContext_quickReject) {
- EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).Times(0);
- EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
- mThreadedRE->useProtectedContext(false);
- // call ANY synchronous function to ensure that useProtectedContext has completed.
- mThreadedRE->getContextPriority();
-}
-
TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) {
EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true));
EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0);
@@ -182,6 +150,68 @@
base::unique_fd bufferFence;
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false));
+ EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+ .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
+
+ ftl::Future<FenceResult> future =
+ mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ ASSERT_TRUE(future.valid());
+ auto result = future.get();
+ ASSERT_TRUE(result.ok());
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers_protectedLayer) {
+ renderengine::DisplaySettings settings;
+ auto layerBuffer = sp<GraphicBuffer>::make();
+ layerBuffer->usage |= GRALLOC_USAGE_PROTECTED;
+ renderengine::LayerSettings layer;
+ layer.source.buffer.buffer = std::make_shared<
+ renderengine::impl::ExternalTexture>(std::move(layerBuffer), *mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::
+ READABLE);
+ std::vector<renderengine::LayerSettings> layers = {std::move(layer)};
+ std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(), *mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+
+ base::unique_fd bufferFence;
+
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(true));
+ EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+ .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
+
+ ftl::Future<FenceResult> future =
+ mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ ASSERT_TRUE(future.valid());
+ auto result = future.get();
+ ASSERT_TRUE(result.ok());
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers_protectedOutputBuffer) {
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ auto graphicBuffer = sp<GraphicBuffer>::make();
+ graphicBuffer->usage |= GRALLOC_USAGE_PROTECTED;
+ std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(std::move(graphicBuffer), *mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+
+ base::unique_fd bufferFence;
+
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(true));
EXPECT_CALL(*mRenderEngine, drawLayersInternal)
.WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const renderengine::DisplaySettings&,
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index b41e843..8aa41b3 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -90,7 +90,6 @@
}
mRenderEngine = factory();
- mIsProtected = mRenderEngine->isProtected();
pthread_setname_np(pthread_self(), mThreadName);
@@ -255,41 +254,11 @@
return mRenderEngine->getMaxViewportDims();
}
-bool RenderEngineThreaded::isProtected() const {
- waitUntilInitialized();
- std::lock_guard lock(mThreadMutex);
- return mIsProtected;
-}
-
bool RenderEngineThreaded::supportsProtectedContent() const {
waitUntilInitialized();
return mRenderEngine->supportsProtectedContent();
}
-void RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
- if (isProtected() == useProtectedContext ||
- (useProtectedContext && !supportsProtectedContent())) {
- return;
- }
-
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([useProtectedContext, this](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::useProtectedContext");
- instance.useProtectedContext(useProtectedContext);
- if (instance.isProtected() != useProtectedContext) {
- ALOGE("Failed to switch RenderEngine context.");
- // reset the cached mIsProtected value to a good state, but this does not
- // prevent other callers of this method and isProtected from reading the
- // invalid cached value.
- mIsProtected = instance.isProtected();
- }
- });
- mIsProtected = useProtectedContext;
- }
- mCondition.notify_one();
-}
-
void RenderEngineThreaded::cleanupPostRender() {
if (canSkipPostRenderCleanup()) {
return;
@@ -334,6 +303,7 @@
mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache,
fd](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::drawLayers");
+ instance.updateProtectedContext(layers, buffer);
instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
useFramebufferCache, base::unique_fd(fd));
});
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index bf2ebea..168e2d2 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -51,9 +51,7 @@
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
- bool isProtected() const override;
bool supportsProtectedContent() const override;
- void useProtectedContext(bool useProtectedContext) override;
void cleanupPostRender() override;
ftl::Future<FenceResult> drawLayers(const DisplaySettings& display,
@@ -84,6 +82,9 @@
void waitUntilInitialized() const;
static status_t setSchedFifo(bool enabled);
+ // No-op. This method is only called on leaf implementations of RenderEngine.
+ void useProtectedContext(bool) override {}
+
/* ------------------------------------------------------------------------
* Threading
*/
@@ -107,7 +108,6 @@
* Render Engine
*/
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
- std::atomic<bool> mIsProtected = false;
};
} // namespace threaded
} // namespace renderengine
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 2b93c6e..b6b9cc4 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,9 +21,10 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libsensor",
+ host_supported: true,
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 78f692b..2278d39 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -42,6 +42,7 @@
GET_DYNAMIC_SENSOR_LIST,
CREATE_SENSOR_DIRECT_CONNECTION,
SET_OPERATION_PARAMETER,
+ GET_RUNTIME_SENSOR_LIST,
};
class BpSensorServer : public BpInterface<ISensorServer>
@@ -90,6 +91,25 @@
return v;
}
+ virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ data.writeString16(opPackageName);
+ data.writeInt32(deviceId);
+ remote()->transact(GET_RUNTIME_SENSOR_LIST, data, &reply);
+ Sensor s;
+ Vector<Sensor> v;
+ uint32_t n = reply.readUint32();
+ v.setCapacity(n);
+ while (n) {
+ n--;
+ reply.read(s);
+ v.add(s);
+ }
+ return v;
+ }
+
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode, const String16& opPackageName, const String16& attributionTag)
{
@@ -194,6 +214,18 @@
}
return NO_ERROR;
}
+ case GET_RUNTIME_SENSOR_LIST: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ const String16& opPackageName = data.readString16();
+ const int deviceId = data.readInt32();
+ Vector<Sensor> v(getRuntimeSensorList(opPackageName, deviceId));
+ size_t n = v.size();
+ reply->writeUint32(static_cast<uint32_t>(n));
+ for (size_t i = 0; i < n; i++) {
+ reply->write(v[i]);
+ }
+ return NO_ERROR;
+ }
case CREATE_SENSOR_DIRECT_CONNECTION: {
CHECK_INTERFACE(ISensorServer, data, reply);
const String16& opPackageName = data.readString16();
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 0ba9704..2748276 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -201,6 +201,19 @@
return static_cast<ssize_t>(count);
}
+ssize_t SensorManager::getRuntimeSensorList(int deviceId, Vector<Sensor>& runtimeSensors) {
+ Mutex::Autolock _l(mLock);
+ status_t err = assertStateLocked();
+ if (err < 0) {
+ return static_cast<ssize_t>(err);
+ }
+
+ runtimeSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, deviceId);
+ size_t count = runtimeSensors.size();
+
+ return static_cast<ssize_t>(count);
+}
+
ssize_t SensorManager::getDynamicSensorList(Sensor const* const** list) {
Mutex::Autolock _l(mLock);
status_t err = assertStateLocked();
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index ce5c672..3295196 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -43,6 +43,7 @@
virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
+ virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) = 0;
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode, const String16& opPackageName, const String16& attributionTag) = 0;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 8d0a8a4..0798da2 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -59,6 +59,7 @@
ssize_t getSensorList(Sensor const* const** list);
ssize_t getDynamicSensorList(Vector<Sensor>& list);
ssize_t getDynamicSensorList(Sensor const* const** list);
+ ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list);
Sensor const* getDefaultSensor(int type);
sp<SensorEventQueue> createEventQueue(
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 2a4a370..42b0cc1 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -68,6 +68,9 @@
// fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
// dataspace for correct conversion to linear colors.
ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+
+ enum SkSLType { Shader, ColorFilter };
+ SkSLType type = Shader;
};
static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
@@ -96,6 +99,10 @@
// 2. Apply color transform matrices in linear space
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+// Generates a shader string that applies color transforms in linear space.
+// This is intended to be plugged into an SkColorFilter
+std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect);
+
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index f80e93f..a3c403e 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -386,12 +386,23 @@
}
}
-void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
- shader.append(R"(
- uniform shader child;
- half4 main(float2 xy) {
- float4 c = float4(child.eval(xy));
- )");
+void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type,
+ std::string& shader) {
+ switch (type) {
+ case LinearEffect::SkSLType::ColorFilter:
+ shader.append(R"(
+ half4 main(half4 inputColor) {
+ float4 c = float4(inputColor);
+ )");
+ break;
+ case LinearEffect::SkSLType::Shader:
+ shader.append(R"(
+ uniform shader child;
+ half4 main(float2 xy) {
+ float4 c = float4(child.eval(xy));
+ )");
+ break;
+ }
if (undoPremultipliedAlpha) {
shader.append(R"(
c.rgb = c.rgb / (c.a + 0.0019);
@@ -459,7 +470,7 @@
generateXYZTransforms(shaderString);
generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, shaderString);
return shaderString;
}
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
index 3306012..6ae27de 100644
--- a/libs/ui/DeviceProductInfo.cpp
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -14,44 +14,43 @@
* limitations under the License.
*/
+#include <ftl/match.h>
#include <ui/DeviceProductInfo.h>
#include <android-base/stringprintf.h>
-#include <utils/Log.h>
-
-#define RETURN_IF_ERROR(op) \
- if (const status_t status = (op); status != OK) return status;
namespace android {
-using base::StringAppendF;
+std::string to_string(const DeviceProductInfo& info) {
+ using base::StringAppendF;
-void DeviceProductInfo::dump(std::string& result) const {
- StringAppendF(&result, "{name=\"%s\", ", name.c_str());
- StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
- StringAppendF(&result, "productId=%s, ", productId.c_str());
+ std::string result;
+ StringAppendF(&result, "{name=\"%s\", ", info.name.c_str());
+ StringAppendF(&result, "manufacturerPnpId=%s, ", info.manufacturerPnpId.data());
+ StringAppendF(&result, "productId=%s, ", info.productId.c_str());
- if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
- StringAppendF(&result, "modelYear=%u, ", model->year);
- } else if (const auto* manufactureWeekAndYear =
- std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
- StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
- StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
- } else if (const auto* manufactureYear =
- std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
- StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
- } else {
- ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
- }
+ ftl::match(
+ info.manufactureOrModelDate,
+ [&](DeviceProductInfo::ModelYear model) {
+ StringAppendF(&result, "modelYear=%u, ", model.year);
+ },
+ [&](DeviceProductInfo::ManufactureWeekAndYear manufacture) {
+ StringAppendF(&result, "manufactureWeek=%u, ", manufacture.week);
+ StringAppendF(&result, "manufactureYear=%d, ", manufacture.year);
+ },
+ [&](DeviceProductInfo::ManufactureYear manufacture) {
+ StringAppendF(&result, "manufactureYear=%d, ", manufacture.year);
+ });
result.append("relativeAddress=[");
- for (size_t i = 0; i < relativeAddress.size(); i++) {
+ for (size_t i = 0; i < info.relativeAddress.size(); i++) {
if (i != 0) {
result.append(", ");
}
- StringAppendF(&result, "%u", relativeAddress[i]);
+ StringAppendF(&result, "%u", info.relativeAddress[i]);
}
result.append("]}");
+ return result;
}
} // namespace android
diff --git a/libs/ui/include/ui/ColorMode.h b/libs/ui/include/ui/ColorMode.h
new file mode 100644
index 0000000..a47eaed
--- /dev/null
+++ b/libs/ui/include/ui/ColorMode.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <vector>
+
+#include <ui/GraphicTypes.h>
+
+namespace android::ui {
+
+using ColorModes = std::vector<ColorMode>;
+
+inline bool isWideColorMode(ColorMode colorMode) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::ADOBE_RGB:
+ case ColorMode::DCI_P3:
+ case ColorMode::BT2020:
+ case ColorMode::DISPLAY_BT2020:
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ return true;
+ case ColorMode::NATIVE:
+ case ColorMode::STANDARD_BT601_625:
+ case ColorMode::STANDARD_BT601_625_UNADJUSTED:
+ case ColorMode::STANDARD_BT601_525:
+ case ColorMode::STANDARD_BT601_525_UNADJUSTED:
+ case ColorMode::STANDARD_BT709:
+ case ColorMode::SRGB:
+ return false;
+ }
+}
+
+inline Dataspace pickDataspaceFor(ColorMode colorMode) {
+ switch (colorMode) {
+ case ColorMode::DISPLAY_P3:
+ case ColorMode::BT2100_PQ:
+ case ColorMode::BT2100_HLG:
+ case ColorMode::DISPLAY_BT2020:
+ return Dataspace::DISPLAY_P3;
+ default:
+ return Dataspace::V0_SRGB;
+ }
+}
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index 879e46f..4229cf1 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -61,8 +61,8 @@
// address is unavailable.
// For example, for HDMI connected device this will be the physical address.
std::vector<uint8_t> relativeAddress;
-
- void dump(std::string& result) const;
};
+std::string to_string(const DeviceProductInfo&);
+
} // namespace android
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index d0c03fe..3a31fa0 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -17,6 +17,7 @@
#pragma once
#include <cstdint>
+#include <ostream>
#include <string>
#include <ftl/optional.h>
@@ -67,6 +68,11 @@
return std::to_string(displayId.value);
}
+// For tests.
+inline std::ostream& operator<<(std::ostream& stream, DisplayId displayId) {
+ return stream << "DisplayId{" << displayId.value << '}';
+}
+
// DisplayId of a physical display, such as the internal display or externally connected display.
struct PhysicalDisplayId : DisplayId {
static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index a2791a6..65a8769 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <type_traits>
+#include <ui/GraphicTypes.h>
#include <ui/Size.h>
#include <utils/Flattenable.h>
#include <utils/Timers.h>
@@ -34,6 +35,7 @@
ui::Size resolution;
float xDpi = 0;
float yDpi = 0;
+ std::vector<ui::Hdr> supportedHdrTypes;
float refreshRate = 0;
nsecs_t appVsyncOffset = 0;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 8c9fe4c..0b77754 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -35,6 +35,7 @@
// we can't use size_t because it may have different width
// in the client process.
ui::DisplayModeId activeDisplayModeId;
+ float renderFrameRate;
std::vector<ui::ColorMode> supportedColorModes;
ui::ColorMode activeColorMode;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 8661c36..1775d39 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -20,6 +20,7 @@
#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
#include <aidl/android/hardware/graphics/common/Compression.h>
#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/common/Interlaced.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
#include <aidl/android/hardware/graphics/common/Smpte2086.h>
@@ -42,7 +43,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
/**
@@ -50,6 +50,7 @@
*/
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Hdr;
using aidl::android::hardware::graphics::common::PlaneLayout;
using aidl::android::hardware::graphics::common::Smpte2086;
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 83d431d..c1d60f4 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -20,7 +20,14 @@
namespace android::ui {
-enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 };
+enum class Rotation {
+ Rotation0 = 0,
+ Rotation90 = 1,
+ Rotation180 = 2,
+ Rotation270 = 3,
+
+ ftl_last = Rotation270
+};
// Equivalent to Surface.java constants.
constexpr auto ROTATION_0 = Rotation::Rotation0;
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 83c250a..2af51a7 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -21,31 +21,8 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library {
- name: "libvibrator",
- vendor_available: true,
- double_loadable: true,
-
- shared_libs: [
- "libbinder",
- "liblog",
- "libutils",
- ],
-
- header_libs: [
- "libaudio_system_headers",
- ],
-
- aidl: {
- include_dirs: ["frameworks/base/core/java"],
- local_include_dirs: ["include/"],
- export_aidl_headers: true,
- },
-
- srcs: [
- ":libvibrator_aidl",
- "*.cpp",
- ],
+cc_defaults {
+ name: "libvibrator_defaults",
cflags: [
"-Wall",
@@ -64,3 +41,54 @@
},
},
}
+
+cc_library {
+ name: "libvibrator",
+ defaults: ["libvibrator_defaults"],
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ whole_static_libs: [
+ "libvibratorutils",
+ ],
+
+ header_libs: [
+ "libaudio_system_headers",
+ ],
+
+ aidl: {
+ include_dirs: ["frameworks/base/core/java"],
+ local_include_dirs: ["include/"],
+ export_aidl_headers: true,
+ },
+
+ srcs: [
+ ":libvibrator_aidl",
+ "ExternalVibration.cpp",
+ ],
+}
+
+cc_library {
+ name: "libvibratorutils",
+ defaults: ["libvibrator_defaults"],
+
+ vendor_available: true,
+ double_loadable: true,
+
+ shared_libs: [
+ "libutils",
+ ],
+
+ srcs: [
+ "ExternalVibrationUtils.cpp",
+ ],
+
+ visibility: [
+ "//frameworks/native/libs/vibrator",
+ "//frameworks/av/media/libeffects/hapticgenerator",
+ ],
+}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index f6fc19e..80e911c 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -15,7 +15,9 @@
*/
#include <vibrator/ExternalVibration.h>
+#include <vibrator/ExternalVibrationUtils.h>
+#include <android/os/IExternalVibratorService.h>
#include <binder/Parcel.h>
#include <log/log.h>
#include <utils/Errors.h>
@@ -63,5 +65,25 @@
return mToken == rhs.mToken;
}
+os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale(int externalVibrationScale) {
+ switch (externalVibrationScale) {
+ case IExternalVibratorService::SCALE_MUTE:
+ return os::HapticScale::MUTE;
+ case IExternalVibratorService::SCALE_VERY_LOW:
+ return os::HapticScale::VERY_LOW;
+ case IExternalVibratorService::SCALE_LOW:
+ return os::HapticScale::LOW;
+ case IExternalVibratorService::SCALE_NONE:
+ return os::HapticScale::NONE;
+ case IExternalVibratorService::SCALE_HIGH:
+ return os::HapticScale::HIGH;
+ case IExternalVibratorService::SCALE_VERY_HIGH:
+ return os::HapticScale::VERY_HIGH;
+ default:
+ ALOGE("Unknown ExternalVibrationScale %d, not applying scaling", externalVibrationScale);
+ return os::HapticScale::NONE;
+ }
+}
+
} // namespace os
} // namespace android
diff --git a/libs/vibrator/include/vibrator/ExternalVibration.h b/libs/vibrator/include/vibrator/ExternalVibration.h
index 760dbce..00cd3cd 100644
--- a/libs/vibrator/include/vibrator/ExternalVibration.h
+++ b/libs/vibrator/include/vibrator/ExternalVibration.h
@@ -23,6 +23,7 @@
#include <binder/Parcelable.h>
#include <system/audio.h>
#include <utils/RefBase.h>
+#include <vibrator/ExternalVibrationUtils.h>
namespace android {
namespace os {
@@ -44,6 +45,10 @@
audio_attributes_t getAudioAttributes() const { return mAttrs; }
sp<IExternalVibrationController> getController() { return mController; }
+ /* Converts the scale from non-public ExternalVibrationService into the HapticScale
+ * used by the utils.
+ */
+ static os::HapticScale externalVibrationScaleToHapticScale(int externalVibrationScale);
private:
int32_t mUid;
@@ -53,7 +58,7 @@
sp<IBinder> mToken = new BBinder();
};
-} // namespace android
} // namespace os
+} // namespace android
#endif // ANDROID_EXTERNAL_VIBRATION_H
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index 84357fc..ca219d3 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -17,17 +17,15 @@
#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
-#include <android/os/IExternalVibratorService.h>
-
namespace android::os {
enum class HapticScale {
- MUTE = IExternalVibratorService::SCALE_MUTE,
- VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW,
- LOW = IExternalVibratorService::SCALE_LOW,
- NONE = IExternalVibratorService::SCALE_NONE,
- HIGH = IExternalVibratorService::SCALE_HIGH,
- VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH,
+ MUTE = -100,
+ VERY_LOW = -2,
+ LOW = -1,
+ NONE = 0,
+ HIGH = 1,
+ VERY_HIGH = 2,
};
bool isValidHapticScale(HapticScale scale);
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
deleted file mode 100644
index 0bda798..0000000
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2016 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"],
-}
-
-sourceFiles = [
- "buffer_hub_queue_client.cpp",
- "buffer_hub_queue_parcelable.cpp",
-]
-
-includeFiles = [
- "include",
-]
-
-staticLibraries = [
- "libbufferhub",
-]
-
-sharedLibraries = [
- "libbinder",
- "libcutils",
- "liblog",
- "libui",
- "libutils",
- "libpdx_default_transport",
-]
-
-headerLibraries = [
- "libdvr_headers",
- "libnativebase_headers",
-]
-
-cc_library_shared {
- name: "libbufferhubqueue",
- cflags: [
- "-DLOG_TAG=\"libbufferhubqueue\"",
- "-DTRACE=0",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-Wall",
- "-Werror",
- "-Wno-format",
- "-Wno-unused-parameter",
- "-Wno-unused-variable",
- ],
- srcs: sourceFiles,
- export_include_dirs: includeFiles,
- export_static_lib_headers: staticLibraries,
- static_libs: staticLibraries,
- shared_libs: sharedLibraries,
- header_libs: headerLibraries,
-}
-
-subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
deleted file mode 100644
index e33e03b..0000000
--- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-
-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_benchmark {
- srcs: ["buffer_transport_benchmark.cpp"],
- shared_libs: [
- "libbase",
- "libbinder",
- "libcutils",
- "libdvr.google",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libbufferhubqueue",
- "libpdx_default_transport",
- ],
- cflags: [
- "-DLOG_TAG=\"buffer_transport_benchmark\"",
- "-DTRACE=0",
- "-O2",
- "-Wall",
- "-Werror",
- ],
- name: "buffer_transport_benchmark",
-}
diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
deleted file mode 100644
index b6813eb..0000000
--- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
+++ /dev/null
@@ -1,589 +0,0 @@
-#include <android-base/logging.h>
-#include <android/native_window.h>
-#include <benchmark/benchmark.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <dvr/dvr_api.h>
-#include <gui/BufferItem.h>
-#include <gui/BufferItemConsumer.h>
-#include <gui/Surface.h>
-#include <private/dvr/epoll_file_descriptor.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <functional>
-#include <iostream>
-#include <thread>
-#include <vector>
-
-#include <dlfcn.h>
-#include <poll.h>
-#include <sys/epoll.h>
-#include <sys/wait.h>
-
-// Use ALWAYS at the tag level. Control is performed manually during command
-// line processing.
-#ifdef ATRACE_TAG
-#undef ATRACE_TAG
-#endif
-#define ATRACE_TAG ATRACE_TAG_ALWAYS
-
-using namespace android;
-using ::benchmark::State;
-
-static const String16 kBinderService = String16("bufferTransport");
-static const uint32_t kBufferWidth = 100;
-static const uint32_t kBufferHeight = 1;
-static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-static const uint64_t kBufferUsage =
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
-static const uint32_t kBufferLayer = 1;
-static const int kMaxAcquiredImages = 1;
-static const int kQueueDepth = 2; // We are double buffering for this test.
-static const size_t kMaxQueueCounts = 128;
-static const int kInvalidFence = -1;
-
-enum BufferTransportServiceCode {
- CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-// A binder services that minics a compositor that consumes buffers. It provides
-// one Binder interface to create a new Surface for buffer producer to write
-// into; while itself will carry out no-op buffer consuming by acquiring then
-// releasing the buffer immediately.
-class BufferTransportService : public BBinder {
- public:
- BufferTransportService() = default;
- ~BufferTransportService() = default;
-
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) {
- (void)flags;
- (void)data;
- switch (code) {
- case CREATE_BUFFER_QUEUE: {
- auto new_queue = std::make_shared<BufferQueueHolder>(this);
- reply->writeStrongBinder(
- IGraphicBufferProducer::asBinder(new_queue->producer));
- buffer_queues_.push_back(new_queue);
- return OK;
- }
- default:
- return UNKNOWN_TRANSACTION;
- };
- }
-
- private:
- struct FrameListener : public ConsumerBase::FrameAvailableListener {
- public:
- FrameListener(BufferTransportService* /*service*/,
- sp<BufferItemConsumer> buffer_item_consumer)
- : buffer_item_consumer_(buffer_item_consumer) {}
-
- void onFrameAvailable(const BufferItem& /*item*/) override {
- BufferItem buffer;
- status_t ret = 0;
- {
- ATRACE_NAME("AcquireBuffer");
- ret = buffer_item_consumer_->acquireBuffer(&buffer, /*presentWhen=*/0,
- /*waitForFence=*/false);
- }
-
- if (ret != OK) {
- LOG(ERROR) << "Failed to acquire next buffer.";
- return;
- }
-
- {
- ATRACE_NAME("ReleaseBuffer");
- ret = buffer_item_consumer_->releaseBuffer(buffer);
- }
-
- if (ret != OK) {
- LOG(ERROR) << "Failed to release buffer.";
- return;
- }
- }
-
- private:
- sp<BufferItemConsumer> buffer_item_consumer_;
- };
-
- struct BufferQueueHolder {
- explicit BufferQueueHolder(BufferTransportService* service) {
- BufferQueue::createBufferQueue(&producer, &consumer);
-
- sp<BufferItemConsumer> buffer_item_consumer =
- new BufferItemConsumer(consumer, kBufferUsage, kMaxAcquiredImages,
- /*controlledByApp=*/true);
- buffer_item_consumer->setName(String8("BinderBufferTransport"));
- frame_listener_ = new FrameListener(service, buffer_item_consumer);
- buffer_item_consumer->setFrameAvailableListener(frame_listener_);
- }
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
-
- private:
- sp<FrameListener> frame_listener_;
- };
-
- std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
-};
-
-// A virtual interfaces that abstracts the common BufferQueue operations, so
-// that the test suite can use the same test case to drive different types of
-// transport backends.
-class BufferTransport {
- public:
- virtual ~BufferTransport() {}
-
- virtual int Start() = 0;
- virtual sp<Surface> CreateSurface() = 0;
-};
-
-// Binder-based buffer transport backend.
-//
-// On Start() a new process will be swapned to run a Binder server that
-// actually consumes the buffer.
-// On CreateSurface() a new Binder BufferQueue will be created, which the
-// service holds the concrete binder node of the IGraphicBufferProducer while
-// sending the binder proxy to the client. In another word, the producer side
-// operations are carried out process while the consumer side operations are
-// carried out within the BufferTransportService's own process.
-class BinderBufferTransport : public BufferTransport {
- public:
- BinderBufferTransport() {}
-
- int Start() override {
- sp<IServiceManager> sm = defaultServiceManager();
- service_ = sm->getService(kBinderService);
- if (service_ == nullptr) {
- LOG(ERROR) << "Failed to get the benchmark service.";
- return -EIO;
- }
-
- LOG(INFO) << "Binder server is ready for client.";
- return 0;
- }
-
- sp<Surface> CreateSurface() override {
- Parcel data;
- Parcel reply;
- int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
- if (error != OK) {
- LOG(ERROR) << "Failed to get buffer queue over binder.";
- return nullptr;
- }
-
- sp<IBinder> binder;
- error = reply.readNullableStrongBinder(&binder);
- if (error != OK) {
- LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
- return nullptr;
- }
-
- auto producer = interface_cast<IGraphicBufferProducer>(binder);
- if (producer == nullptr) {
- LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
- return nullptr;
- }
-
- sp<Surface> surface = new Surface(producer, /*controlledByApp=*/true);
-
- // Set buffer dimension.
- ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
- ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight,
- kBufferFormat);
-
- return surface;
- }
-
- private:
- sp<IBinder> service_;
-};
-
-class DvrApi {
- public:
- DvrApi() {
- handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL);
- CHECK(handle_);
-
- auto dvr_get_api =
- reinterpret_cast<decltype(&dvrGetApi)>(dlsym(handle_, "dvrGetApi"));
- int ret = dvr_get_api(&api_, sizeof(api_), /*version=*/1);
-
- CHECK(ret == 0);
- }
-
- ~DvrApi() { dlclose(handle_); }
-
- const DvrApi_v1& Api() { return api_; }
-
- private:
- void* handle_ = nullptr;
- DvrApi_v1 api_;
-};
-
-// BufferHub/PDX-based buffer transport.
-//
-// On Start() a new thread will be swapned to run an epoll polling thread which
-// minics the behavior of a compositor. Similar to Binder-based backend, the
-// buffer available handler is also a no-op: Buffer gets acquired and released
-// immediately.
-// On CreateSurface() a pair of dvr::ProducerQueue and dvr::ConsumerQueue will
-// be created. The epoll thread holds on the consumer queue and dequeues buffer
-// from it; while the producer queue will be wrapped in a Surface and returned
-// to test suite.
-class BufferHubTransport : public BufferTransport {
- public:
- virtual ~BufferHubTransport() {
- stopped_.store(true);
- if (reader_thread_.joinable()) {
- reader_thread_.join();
- }
- }
-
- int Start() override {
- int ret = epoll_fd_.Create();
- if (ret < 0) {
- LOG(ERROR) << "Failed to create epoll fd: %s", strerror(-ret);
- return -1;
- }
-
- // Create the reader thread.
- reader_thread_ = std::thread([this]() {
- int ret = dvr_.Api().PerformanceSetSchedulerPolicy(0, "graphics");
- if (ret < 0) {
- LOG(ERROR) << "Failed to set scheduler policy, ret=" << ret;
- return;
- }
-
- stopped_.store(false);
- LOG(INFO) << "Reader Thread Running...";
-
- while (!stopped_.load()) {
- std::array<epoll_event, kMaxQueueCounts> events;
-
- // Don't sleep forever so that we will have a chance to wake up.
- const int ret = epoll_fd_.Wait(events.data(), events.size(),
- /*timeout=*/100);
- if (ret < 0) {
- LOG(ERROR) << "Error polling consumer queues.";
- continue;
- }
- if (ret == 0) {
- continue;
- }
-
- const int num_events = ret;
- for (int i = 0; i < num_events; i++) {
- uint32_t index = events[i].data.u32;
- dvr_.Api().ReadBufferQueueHandleEvents(
- buffer_queues_[index]->GetReadQueue());
- }
- }
-
- LOG(INFO) << "Reader Thread Exiting...";
- });
-
- return 0;
- }
-
- sp<Surface> CreateSurface() override {
- auto new_queue = std::make_shared<BufferQueueHolder>();
- if (!new_queue->IsReady()) {
- LOG(ERROR) << "Failed to create BufferHub-based BufferQueue.";
- return nullptr;
- }
-
- // Set buffer dimension.
- ANativeWindow_setBuffersGeometry(new_queue->GetSurface(), kBufferWidth,
- kBufferHeight, kBufferFormat);
-
- // Use the next position as buffer_queue index.
- uint32_t index = buffer_queues_.size();
- epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u32 = index}};
- int queue_fd =
- dvr_.Api().ReadBufferQueueGetEventFd(new_queue->GetReadQueue());
- const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, queue_fd, &event);
- if (ret < 0) {
- LOG(ERROR) << "Failed to track consumer queue: " << strerror(-ret)
- << ", consumer queue fd: " << queue_fd;
- return nullptr;
- }
-
- buffer_queues_.push_back(new_queue);
- ANativeWindow_acquire(new_queue->GetSurface());
- return static_cast<Surface*>(new_queue->GetSurface());
- }
-
- private:
- struct BufferQueueHolder {
- BufferQueueHolder() {
- int ret = 0;
- ret = dvr_.Api().WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kBufferLayer,
- kBufferUsage, 0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create write buffer queue, ret=" << ret;
- return;
- }
-
- ret = dvr_.Api().WriteBufferQueueCreateReadQueue(write_queue_,
- &read_queue_);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create read buffer queue, ret=" << ret;
- return;
- }
-
- ret = dvr_.Api().ReadBufferQueueSetBufferAvailableCallback(
- read_queue_, BufferAvailableCallback, this);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create buffer available callback, ret=" << ret;
- return;
- }
-
- ret =
- dvr_.Api().WriteBufferQueueGetANativeWindow(write_queue_, &surface_);
- if (ret < 0) {
- LOG(ERROR) << "Failed to create surface, ret=" << ret;
- return;
- }
- }
-
- static void BufferAvailableCallback(void* context) {
- BufferQueueHolder* thiz = static_cast<BufferQueueHolder*>(context);
- thiz->HandleBufferAvailable();
- }
-
- DvrReadBufferQueue* GetReadQueue() { return read_queue_; }
-
- ANativeWindow* GetSurface() { return surface_; }
-
- bool IsReady() {
- return write_queue_ != nullptr && read_queue_ != nullptr &&
- surface_ != nullptr;
- }
-
- void HandleBufferAvailable() {
- int ret = 0;
- DvrNativeBufferMetadata meta;
- DvrReadBuffer* buffer = nullptr;
- DvrNativeBufferMetadata metadata;
- int acquire_fence = kInvalidFence;
-
- {
- ATRACE_NAME("AcquireBuffer");
- ret = dvr_.Api().ReadBufferQueueAcquireBuffer(
- read_queue_, 0, &buffer, &metadata, &acquire_fence);
- }
- if (ret < 0) {
- LOG(ERROR) << "Failed to acquire consumer buffer, error: " << ret;
- return;
- }
-
- if (buffer != nullptr) {
- ATRACE_NAME("ReleaseBuffer");
- ret = dvr_.Api().ReadBufferQueueReleaseBuffer(read_queue_, buffer,
- &meta, kInvalidFence);
- }
- if (ret < 0) {
- LOG(ERROR) << "Failed to release consumer buffer, error: " << ret;
- }
- }
-
- private:
- DvrWriteBufferQueue* write_queue_ = nullptr;
- DvrReadBufferQueue* read_queue_ = nullptr;
- ANativeWindow* surface_ = nullptr;
- };
-
- static DvrApi dvr_;
- std::atomic<bool> stopped_;
- std::thread reader_thread_;
-
- dvr::EpollFileDescriptor epoll_fd_;
- std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
-};
-
-DvrApi BufferHubTransport::dvr_ = {};
-
-enum TransportType {
- kBinderBufferTransport,
- kBufferHubTransport,
-};
-
-// Main test suite, which supports two transport backend: 1) BinderBufferQueue,
-// 2) BufferHubQueue. The test case drives the producer end of both transport
-// backend by queuing buffers into the buffer queue by using ANativeWindow API.
-class BufferTransportBenchmark : public ::benchmark::Fixture {
- public:
- void SetUp(State& state) override {
- if (state.thread_index == 0) {
- const int transport = state.range(0);
- switch (transport) {
- case kBinderBufferTransport:
- transport_.reset(new BinderBufferTransport);
- break;
- case kBufferHubTransport:
- transport_.reset(new BufferHubTransport);
- break;
- default:
- CHECK(false) << "Unknown test case.";
- break;
- }
-
- CHECK(transport_);
- const int ret = transport_->Start();
- CHECK_EQ(ret, 0);
-
- LOG(INFO) << "Transport backend running, transport=" << transport << ".";
-
- // Create surfaces for each thread.
- surfaces_.resize(state.threads);
- for (int i = 0; i < state.threads; i++) {
- // Common setup every thread needs.
- surfaces_[i] = transport_->CreateSurface();
- CHECK(surfaces_[i]);
-
- LOG(INFO) << "Surface initialized on thread " << i << ".";
- }
- }
- }
-
- void TearDown(State& state) override {
- if (state.thread_index == 0) {
- surfaces_.clear();
- transport_.reset();
- LOG(INFO) << "Tear down benchmark.";
- }
- }
-
- protected:
- std::unique_ptr<BufferTransport> transport_;
- std::vector<sp<Surface>> surfaces_;
-};
-
-BENCHMARK_DEFINE_F(BufferTransportBenchmark, Producers)(State& state) {
- ANativeWindow* window = nullptr;
- ANativeWindow_Buffer buffer;
- int32_t error = 0;
- double total_gain_buffer_us = 0;
- double total_post_buffer_us = 0;
- int iterations = 0;
-
- while (state.KeepRunning()) {
- if (window == nullptr) {
- CHECK(surfaces_[state.thread_index]);
- window = static_cast<ANativeWindow*>(surfaces_[state.thread_index].get());
-
- // Lock buffers a couple time from the queue, so that we have the buffer
- // allocated.
- for (int i = 0; i < kQueueDepth; i++) {
- error = ANativeWindow_lock(window, &buffer,
- /*inOutDirtyBounds=*/nullptr);
- CHECK_EQ(error, 0);
- error = ANativeWindow_unlockAndPost(window);
- CHECK_EQ(error, 0);
- }
- }
-
- {
- ATRACE_NAME("GainBuffer");
- auto t1 = std::chrono::high_resolution_clock::now();
- error = ANativeWindow_lock(window, &buffer,
- /*inOutDirtyBounds=*/nullptr);
- auto t2 = std::chrono::high_resolution_clock::now();
- std::chrono::duration<double, std::micro> delta_us = t2 - t1;
- total_gain_buffer_us += delta_us.count();
- }
- CHECK_EQ(error, 0);
-
- {
- ATRACE_NAME("PostBuffer");
- auto t1 = std::chrono::high_resolution_clock::now();
- error = ANativeWindow_unlockAndPost(window);
- auto t2 = std::chrono::high_resolution_clock::now();
- std::chrono::duration<double, std::micro> delta_us = t2 - t1;
- total_post_buffer_us += delta_us.count();
- }
- CHECK_EQ(error, 0);
-
- iterations++;
- }
-
- state.counters["gain_buffer_us"] = ::benchmark::Counter(
- total_gain_buffer_us / iterations, ::benchmark::Counter::kAvgThreads);
- state.counters["post_buffer_us"] = ::benchmark::Counter(
- total_post_buffer_us / iterations, ::benchmark::Counter::kAvgThreads);
- state.counters["producer_us"] = ::benchmark::Counter(
- (total_gain_buffer_us + total_post_buffer_us) / iterations,
- ::benchmark::Counter::kAvgThreads);
-}
-
-BENCHMARK_REGISTER_F(BufferTransportBenchmark, Producers)
- ->Unit(::benchmark::kMicrosecond)
- ->Ranges({{kBinderBufferTransport, kBufferHubTransport}})
- ->ThreadRange(1, 32);
-
-static void runBinderServer() {
- ProcessState::self()->setThreadPoolMaxThreadCount(0);
- ProcessState::self()->startThreadPool();
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<BufferTransportService> service = new BufferTransportService;
- sm->addService(kBinderService, service, false);
-
- LOG(INFO) << "Binder server running...";
-
- while (true) {
- int stat, retval;
- retval = wait(&stat);
- if (retval == -1 && errno == ECHILD) {
- break;
- }
- }
-
- LOG(INFO) << "Service Exiting...";
-}
-
-// To run binder-based benchmark, use:
-// adb shell buffer_transport_benchmark \
-// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/0/"
-//
-// To run bufferhub-based benchmark, use:
-// adb shell buffer_transport_benchmark \
-// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/1/"
-int main(int argc, char** argv) {
- bool tracing_enabled = false;
-
- // Parse arguments in addition to "--benchmark_filter" paramters.
- for (int i = 1; i < argc; i++) {
- if (std::string(argv[i]) == "--help") {
- std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl;
- std::cout << "\t--trace: Enable systrace logging." << std::endl;
- return 0;
- }
- if (std::string(argv[i]) == "--trace") {
- tracing_enabled = true;
- continue;
- }
- }
-
- // Setup ATRACE/systrace based on command line.
- atrace_setup();
- atrace_set_tracing_enabled(tracing_enabled);
-
- pid_t pid = fork();
- if (pid == 0) {
- // Child, i.e. the client side.
- ProcessState::self()->startThreadPool();
-
- ::benchmark::Initialize(&argc, argv);
- ::benchmark::RunSpecifiedBenchmarks();
- } else {
- LOG(INFO) << "Benchmark process pid: " << pid;
- runBinderServer();
- }
-}
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
deleted file mode 100644
index 2d3fa4a..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ /dev/null
@@ -1,823 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_client.h"
-
-#include <inttypes.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/epoll.h>
-
-#include <array>
-
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <pdx/trace.h>
-
-#define RETRY_EINTR(fnc_call) \
- ([&]() -> decltype(fnc_call) { \
- decltype(fnc_call) result; \
- do { \
- result = (fnc_call); \
- } while (result == -1 && errno == EINTR); \
- return result; \
- })()
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-std::pair<int32_t, int32_t> Unstuff(uint64_t value) {
- return {static_cast<int32_t>(value >> 32),
- static_cast<int32_t>(value & ((1ull << 32) - 1))};
-}
-
-uint64_t Stuff(int32_t a, int32_t b) {
- const uint32_t ua = static_cast<uint32_t>(a);
- const uint32_t ub = static_cast<uint32_t>(b);
- return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub);
-}
-
-} // anonymous namespace
-
-BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle)
- : Client{pdx::default_transport::ClientChannel::Create(
- std::move(channel_handle))} {
- Initialize();
-}
-
-BufferHubQueue::BufferHubQueue(const std::string& endpoint_path)
- : Client{
- pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} {
- Initialize();
-}
-
-void BufferHubQueue::Initialize() {
- int ret = epoll_fd_.Create();
- if (ret < 0) {
- ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
- strerror(-ret));
- return;
- }
-
- epoll_event event = {
- .events = EPOLLIN | EPOLLET,
- .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}};
- ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
- if (ret < 0) {
- ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__,
- strerror(-ret));
- }
-}
-
-Status<void> BufferHubQueue::ImportQueue() {
- auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
- if (!status) {
- ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return ErrorStatus(status.error());
- } else {
- SetupQueue(status.get());
- return {};
- }
-}
-
-void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) {
- is_async_ = queue_info.producer_config.is_async;
- default_width_ = queue_info.producer_config.default_width;
- default_height_ = queue_info.producer_config.default_height;
- default_format_ = queue_info.producer_config.default_format;
- user_metadata_size_ = queue_info.producer_config.user_metadata_size;
- id_ = queue_info.id;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
- if (auto status = CreateConsumerQueueHandle(/*silent*/ false))
- return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
- else
- return nullptr;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() {
- if (auto status = CreateConsumerQueueHandle(/*silent*/ true))
- return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
- else
- return nullptr;
-}
-
-Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle(
- bool silent) {
- auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent);
- if (!status) {
- ALOGE(
- "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
- "%s",
- status.GetErrorMessage().c_str());
- return ErrorStatus(status.error());
- }
-
- return status;
-}
-
-pdx::Status<ConsumerQueueParcelable>
-BufferHubQueue::CreateConsumerQueueParcelable(bool silent) {
- auto status = CreateConsumerQueueHandle(silent);
- if (!status)
- return status.error_status();
-
- // A temporary consumer queue client to pull its channel parcelable.
- auto consumer_queue =
- std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
- ConsumerQueueParcelable queue_parcelable(
- consumer_queue->GetChannel()->TakeChannelParcelable());
-
- if (!queue_parcelable.IsValid()) {
- ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__);
- return ErrorStatus(EINVAL);
- }
-
- return {std::move(queue_parcelable)};
-}
-
-bool BufferHubQueue::WaitForBuffers(int timeout) {
- ATRACE_NAME("BufferHubQueue::WaitForBuffers");
- std::array<epoll_event, kMaxEvents> events;
-
- // Loop at least once to check for hangups.
- do {
- ALOGD_IF(
- TRACE,
- "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu",
- id(), count(), capacity());
-
- // If there is already a buffer then just check for hangup without waiting.
- const int ret = epoll_fd_.Wait(events.data(), events.size(),
- count() == 0 ? timeout : 0);
-
- if (ret == 0) {
- ALOGI_IF(TRACE,
- "BufferHubQueue::WaitForBuffers: No events before timeout: "
- "queue_id=%d",
- id());
- return count() != 0;
- }
-
- if (ret < 0 && ret != -EINTR) {
- ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret));
- return false;
- }
-
- const int num_events = ret;
-
- // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
- // one for each buffer in the queue, and one extra event for the queue
- // client itself.
- for (int i = 0; i < num_events; i++) {
- int32_t event_fd;
- int32_t index;
- std::tie(event_fd, index) = Unstuff(events[i].data.u64);
-
- PDX_TRACE_FORMAT(
- "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;"
- "slot=%d|",
- id(), num_events, i, event_fd, index);
-
- ALOGD_IF(TRACE,
- "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d",
- i, event_fd, index);
-
- if (is_buffer_event_index(index)) {
- HandleBufferEvent(static_cast<size_t>(index), event_fd,
- events[i].events);
- } else if (is_queue_event_index(index)) {
- HandleQueueEvent(events[i].events);
- } else {
- ALOGW(
- "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d "
- "index=%d",
- event_fd, index);
- }
- }
- } while (count() == 0 && capacity() > 0 && !hung_up());
-
- return count() != 0;
-}
-
-Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd,
- int poll_events) {
- ATRACE_NAME("BufferHubQueue::HandleBufferEvent");
- if (!buffers_[slot]) {
- ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
- return ErrorStatus(ENOENT);
- }
-
- auto status = buffers_[slot]->GetEventMask(poll_events);
- if (!status) {
- ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- const int events = status.get();
- PDX_TRACE_FORMAT(
- "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;"
- "events=%d|",
- id(), buffers_[slot]->id(), slot, event_fd, poll_events, events);
-
- if (events & EPOLLIN) {
- return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()});
- } else if (events & EPOLLHUP) {
- ALOGW(
- "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu "
- "event_fd=%d buffer_id=%d",
- slot, buffers_[slot]->event_fd(), buffers_[slot]->id());
- return RemoveBuffer(slot);
- } else {
- ALOGW(
- "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
- "events=%d",
- slot, events);
- }
-
- return {};
-}
-
-Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) {
- ATRACE_NAME("BufferHubQueue::HandleQueueEvent");
- auto status = GetEventMask(poll_event);
- if (!status) {
- ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- const int events = status.get();
- if (events & EPOLLIN) {
- // Note that after buffer imports, if |count()| still returns 0, epoll
- // wait will be tried again to acquire the newly imported buffer.
- auto buffer_status = OnBufferAllocated();
- if (!buffer_status) {
- ALOGE("%s: Failed to import buffer: %s", __FUNCTION__,
- buffer_status.GetErrorMessage().c_str());
- }
- } else if (events & EPOLLHUP) {
- ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__);
- hung_up_ = true;
- } else {
- ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events);
- }
-
- return {};
-}
-
-Status<void> BufferHubQueue::AddBuffer(
- const std::shared_ptr<BufferHubBase>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(),
- slot);
-
- if (is_full()) {
- ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_);
- return ErrorStatus(E2BIG);
- }
-
- if (buffers_[slot]) {
- // Replace the buffer if the slot is occupied. This could happen when the
- // producer side replaced the slot with a newly allocated buffer. Remove the
- // buffer before setting up with the new one.
- auto remove_status = RemoveBuffer(slot);
- if (!remove_status)
- return remove_status.error_status();
- }
-
- for (const auto& event_source : buffer->GetEventSources()) {
- epoll_event event = {.events = event_source.event_mask | EPOLLET,
- .data = {.u64 = Stuff(buffer->event_fd(), slot)}};
- const int ret =
- epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
- if (ret < 0) {
- ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__,
- strerror(-ret));
- return ErrorStatus(-ret);
- }
- }
-
- buffers_[slot] = buffer;
- capacity_++;
- return {};
-}
-
-Status<void> BufferHubQueue::RemoveBuffer(size_t slot) {
- ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot);
-
- if (buffers_[slot]) {
- for (const auto& event_source : buffers_[slot]->GetEventSources()) {
- const int ret =
- epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
- if (ret < 0) {
- ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__,
- strerror(-ret));
- return ErrorStatus(-ret);
- }
- }
-
- // Trigger OnBufferRemoved callback if registered.
- if (on_buffer_removed_)
- on_buffer_removed_(buffers_[slot]);
-
- buffers_[slot] = nullptr;
- capacity_--;
- }
-
- return {};
-}
-
-Status<void> BufferHubQueue::Enqueue(Entry entry) {
- if (!is_full()) {
- // Find and remove the enqueued buffer from unavailable_buffers_slot if
- // exist.
- auto enqueued_buffer_iter = std::find_if(
- unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(),
- [&entry](size_t slot) -> bool { return slot == entry.slot; });
- if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) {
- unavailable_buffers_slot_.erase(enqueued_buffer_iter);
- }
-
- available_buffers_.push(std::move(entry));
-
- // Trigger OnBufferAvailable callback if registered.
- if (on_buffer_available_)
- on_buffer_available_();
-
- return {};
- } else {
- ALOGE("%s: Buffer queue is full!", __FUNCTION__);
- return ErrorStatus(E2BIG);
- }
-}
-
-Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout,
- size_t* slot) {
- ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout);
-
- PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count());
-
- if (count() == 0) {
- if (!WaitForBuffers(timeout))
- return ErrorStatus(ETIMEDOUT);
- }
-
- auto& entry = available_buffers_.top();
- PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
- entry.slot);
-
- std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer);
- *slot = entry.slot;
-
- available_buffers_.pop();
- unavailable_buffers_slot_.push_back(*slot);
-
- return {std::move(buffer)};
-}
-
-void BufferHubQueue::SetBufferAvailableCallback(
- BufferAvailableCallback callback) {
- on_buffer_available_ = callback;
-}
-
-void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) {
- on_buffer_removed_ = callback;
-}
-
-pdx::Status<void> BufferHubQueue::FreeAllBuffers() {
- // Clear all available buffers.
- while (!available_buffers_.empty())
- available_buffers_.pop();
-
- pdx::Status<void> last_error; // No error.
- // Clear all buffers this producer queue is tracking.
- for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
- if (buffers_[slot] != nullptr) {
- auto status = RemoveBuffer(slot);
- if (!status) {
- ALOGE(
- "ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
- "slot=%zu.",
- slot);
- last_error = status.error_status();
- }
- }
- }
-
- return last_error;
-}
-
-ProducerQueue::ProducerQueue(LocalChannelHandle handle)
- : BASE(std::move(handle)) {
- auto status = ImportQueue();
- if (!status) {
- ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s",
- status.GetErrorMessage().c_str());
- Close(-status.error());
- }
-}
-
-ProducerQueue::ProducerQueue(const ProducerQueueConfig& config,
- const UsagePolicy& usage)
- : BASE(BufferHubRPC::kClientPath) {
- auto status =
- InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage);
- if (!status) {
- ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
- status.GetErrorMessage().c_str());
- Close(-status.error());
- return;
- }
-
- SetupQueue(status.get());
-}
-
-Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
- uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t buffer_count) {
- if (buffer_count == 0) {
- return {std::vector<size_t>()};
- }
-
- if (capacity() + buffer_count > kMaxQueueCapacity) {
- ALOGE(
- "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
- "allocate %zu more buffer(s).",
- capacity(), buffer_count);
- return ErrorStatus(E2BIG);
- }
-
- Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
- InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
- width, height, layer_count, format, usage, buffer_count);
- if (!status) {
- ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto buffer_handle_slots = status.take();
- LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count,
- "BufferHubRPC::ProducerQueueAllocateBuffers should "
- "return %zu buffer handle(s), but returned %zu instead.",
- buffer_count, buffer_handle_slots.size());
-
- std::vector<size_t> buffer_slots;
- buffer_slots.reserve(buffer_count);
-
- // Bookkeeping for each buffer.
- for (auto& hs : buffer_handle_slots) {
- auto& buffer_handle = hs.first;
- size_t buffer_slot = hs.second;
-
- // Note that import might (though very unlikely) fail. If so, buffer_handle
- // will be closed and included in returned buffer_slots.
- if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)),
- buffer_slot)) {
- ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
- buffer_slot);
- buffer_slots.push_back(buffer_slot);
- }
- }
-
- if (buffer_slots.size() != buffer_count) {
- // Error out if the count of imported buffer(s) is not correct.
- ALOGE(
- "ProducerQueue::AllocateBuffers: requested to import %zu "
- "buffers, but actually imported %zu buffers.",
- buffer_count, buffer_slots.size());
- return ErrorStatus(ENOMEM);
- }
-
- return {std::move(buffer_slots)};
-}
-
-Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
- uint32_t layer_count,
- uint32_t format, uint64_t usage) {
- // We only allocate one buffer at a time.
- constexpr size_t buffer_count = 1;
- auto status =
- AllocateBuffers(width, height, layer_count, format, usage, buffer_count);
- if (!status) {
- ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return {status.get()[0]};
-}
-
-Status<void> ProducerQueue::AddBuffer(
- const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
- id(), buffer->id(), slot);
- // For producer buffer, we need to enqueue the newly added buffer
- // immediately. Producer queue starts with all buffers in available state.
- auto status = BufferHubQueue::AddBuffer(buffer, slot);
- if (!status)
- return status;
-
- return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
-}
-
-Status<size_t> ProducerQueue::InsertBuffer(
- const std::shared_ptr<ProducerBuffer>& buffer) {
- if (buffer == nullptr ||
- !BufferHubDefs::isClientGained(buffer->buffer_state(),
- buffer->client_state_mask())) {
- ALOGE(
- "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
- "gained state.");
- return ErrorStatus(EINVAL);
- }
-
- auto status_or_slot =
- InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
- buffer->cid());
- if (!status_or_slot) {
- ALOGE(
- "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
- "buffer_cid=%d, error: %s.",
- buffer->cid(), status_or_slot.GetErrorMessage().c_str());
- return status_or_slot.error_status();
- }
-
- size_t slot = status_or_slot.get();
-
- // Note that we are calling AddBuffer() from the base class to explicitly
- // avoid Enqueue() the ProducerBuffer.
- auto status = BufferHubQueue::AddBuffer(buffer, slot);
- if (!status) {
- ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
- return {slot};
-}
-
-Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
- auto status =
- InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
- if (!status) {
- ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return BufferHubQueue::RemoveBuffer(slot);
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
- int timeout, size_t* slot, LocalHandle* release_fence) {
- DvrNativeBufferMetadata canonical_meta;
- return Dequeue(timeout, slot, &canonical_meta, release_fence);
-}
-
-pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* release_fence, bool gain_posted_buffer) {
- ATRACE_NAME("ProducerQueue::Dequeue");
- if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
- ALOGE("%s: Invalid parameter.", __FUNCTION__);
- return ErrorStatus(EINVAL);
- }
-
- std::shared_ptr<ProducerBuffer> buffer;
- Status<std::shared_ptr<BufferHubBase>> dequeue_status =
- BufferHubQueue::Dequeue(timeout, slot);
- if (dequeue_status.ok()) {
- buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take());
- } else {
- if (gain_posted_buffer) {
- Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status =
- ProducerQueue::DequeueUnacquiredBuffer(slot);
- if (!dequeue_unacquired_status.ok()) {
- ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__,
- dequeue_unacquired_status.error());
- return dequeue_unacquired_status.error_status();
- }
- buffer = dequeue_unacquired_status.take();
- } else {
- return dequeue_status.error_status();
- }
- }
- const int ret =
- buffer->GainAsync(out_meta, release_fence, gain_posted_buffer);
- if (ret < 0 && ret != -EALREADY)
- return ErrorStatus(-ret);
-
- return {std::move(buffer)};
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer(
- size_t* slot) {
- if (unavailable_buffers_slot_.size() < 1) {
- ALOGE(
- "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in "
- "acquired state if exist.",
- __FUNCTION__);
- return ErrorStatus(ENOMEM);
- }
-
- // Find the first buffer that is not in acquired state from
- // unavailable_buffers_slot_.
- for (auto iter = unavailable_buffers_slot_.begin();
- iter != unavailable_buffers_slot_.end(); iter++) {
- std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter);
- if (buffer == nullptr) {
- ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__,
- static_cast<int>(*slot));
- return ErrorStatus(EIO);
- }
- if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) {
- *slot = *iter;
- unavailable_buffers_slot_.erase(iter);
- unavailable_buffers_slot_.push_back(*slot);
- ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d",
- __FUNCTION__, static_cast<int>(*slot));
- return {std::move(buffer)};
- }
- }
- ALOGE(
- "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.",
- __FUNCTION__);
- return ErrorStatus(EBUSY);
-}
-
-pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
- if (capacity() != 0) {
- ALOGE(
- "%s: producer queue can only be taken out as a parcelable when empty. "
- "Current queue capacity: %zu",
- __FUNCTION__, capacity());
- return ErrorStatus(EINVAL);
- }
-
- std::unique_ptr<pdx::ClientChannel> channel = TakeChannel();
- ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable());
-
- // Here the queue parcelable is returned and holds the underlying system
- // resources backing the queue; while the original client channel of this
- // producer queue is destroyed in place so that this client can no longer
- // provide producer operations.
- return {std::move(queue_parcelable)};
-}
-
-/*static */
-std::unique_ptr<ConsumerQueue> ConsumerQueue::Import(
- LocalChannelHandle handle) {
- return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle)));
-}
-
-ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
- : BufferHubQueue(std::move(handle)) {
- auto status = ImportQueue();
- if (!status) {
- ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- Close(-status.error());
- }
-
- auto import_status = ImportBuffers();
- if (import_status) {
- ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get());
- } else {
- ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
- import_status.GetErrorMessage().c_str());
- }
-}
-
-Status<size_t> ConsumerQueue::ImportBuffers() {
- auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
- if (!status) {
- if (status.error() == EBADR) {
- ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__);
- return {0};
- } else {
- ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
- }
-
- int ret;
- Status<void> last_error;
- size_t imported_buffers_count = 0;
-
- auto buffer_handle_slots = status.take();
- for (auto& buffer_handle_slot : buffer_handle_slots) {
- ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__,
- buffer_handle_slot.first.value());
-
- std::unique_ptr<ConsumerBuffer> consumer_buffer =
- ConsumerBuffer::Import(std::move(buffer_handle_slot.first));
- if (!consumer_buffer) {
- ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__,
- buffer_handle_slot.second);
- last_error = ErrorStatus(EPIPE);
- continue;
- }
-
- auto add_status =
- AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second);
- if (!add_status) {
- ALOGE("%s: Failed to add buffer: %s", __FUNCTION__,
- add_status.GetErrorMessage().c_str());
- last_error = add_status;
- } else {
- imported_buffers_count++;
- }
- }
-
- if (imported_buffers_count > 0)
- return {imported_buffers_count};
- else
- return last_error.error_status();
-}
-
-Status<void> ConsumerQueue::AddBuffer(
- const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(),
- buffer->id(), slot);
- return BufferHubQueue::AddBuffer(buffer, slot);
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
- int timeout, size_t* slot, void* meta, size_t user_metadata_size,
- LocalHandle* acquire_fence) {
- if (user_metadata_size != user_metadata_size_) {
- ALOGE(
- "%s: Metadata size (%zu) for the dequeuing buffer does not match "
- "metadata size (%zu) for the queue.",
- __FUNCTION__, user_metadata_size, user_metadata_size_);
- return ErrorStatus(EINVAL);
- }
-
- DvrNativeBufferMetadata canonical_meta;
- auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence);
- if (!status)
- return status.error_status();
-
- if (meta && user_metadata_size) {
- void* metadata_src =
- reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
- if (metadata_src) {
- memcpy(meta, metadata_src, user_metadata_size);
- } else {
- ALOGW("%s: no user-defined metadata.", __FUNCTION__);
- }
- }
-
- return status;
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* acquire_fence) {
- ATRACE_NAME("ConsumerQueue::Dequeue");
- if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
- ALOGE("%s: Invalid parameter.", __FUNCTION__);
- return ErrorStatus(EINVAL);
- }
-
- auto status = BufferHubQueue::Dequeue(timeout, slot);
- if (!status)
- return status.error_status();
-
- auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take());
- const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
- if (ret < 0)
- return ErrorStatus(-ret);
-
- return {std::move(buffer)};
-}
-
-Status<void> ConsumerQueue::OnBufferAllocated() {
- ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id());
-
- auto status = ImportBuffers();
- if (!status) {
- ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return ErrorStatus(status.error());
- } else if (status.get() == 0) {
- ALOGW("%s: No new buffers allocated!", __FUNCTION__);
- return ErrorStatus(ENOBUFS);
- } else {
- ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__,
- status.get());
- return {};
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
deleted file mode 100644
index f705749..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_parcelable.h"
-
-#include <binder/Parcel.h>
-#include <pdx/default_transport/channel_parcelable.h>
-
-namespace android {
-namespace dvr {
-
-template <BufferHubQueueParcelableMagic Magic>
-bool BufferHubQueueParcelable<Magic>::IsValid() const {
- return !!channel_parcelable_ && channel_parcelable_->IsValid();
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() {
- if (!IsValid()) {
- ALOGE(
- "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel.");
- return {}; // Returns an empty channel handle.
- }
-
- // Take channel handle out of the parcelable and reset the parcelable.
- pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle();
- // Now channel_parcelable_ should already be invalid, but reset it to release
- // the invalid parcelable object from unique_ptr.
- channel_parcelable_ = nullptr;
- return handle;
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const {
- if (!IsValid()) {
- ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel.");
- return -EINVAL;
- }
-
- status_t res = parcel->writeUint32(Magic);
- if (res != OK) {
- ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
- return res;
- }
-
- return channel_parcelable_->writeToParcel(parcel);
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) {
- if (IsValid()) {
- ALOGE(
- "BufferHubQueueParcelable::readFromParcel: This parcelable object has "
- "been initialized already.");
- return -EINVAL;
- }
-
- uint32_t out_magic = 0;
- status_t res = OK;
-
- res = parcel->readUint32(&out_magic);
- if (res != OK)
- return res;
-
- if (out_magic != Magic) {
- ALOGE(
- "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, "
- "epxected: 0x%x",
- out_magic, Magic);
- return -EINVAL;
- }
-
- // (Re)Alocate channel parcelable object.
- channel_parcelable_ =
- std::make_unique<pdx::default_transport::ChannelParcelable>();
- return channel_parcelable_->readFromParcel(parcel);
-}
-
-template class BufferHubQueueParcelable<
- BufferHubQueueParcelableMagic::Producer>;
-template class BufferHubQueueParcelable<
- BufferHubQueueParcelableMagic::Consumer>;
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
deleted file mode 100644
index 74b4b3d..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ /dev/null
@@ -1,476 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-
-#include <ui/BufferQueueDefs.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/epoll_file_descriptor.h>
-#include <private/dvr/producer_buffer.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-#include <memory>
-#include <queue>
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-class ConsumerQueue;
-
-// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are
-// automatically re-requeued when released by the remote side.
-class BufferHubQueue : public pdx::Client {
- public:
- using BufferAvailableCallback = std::function<void()>;
- using BufferRemovedCallback =
- std::function<void(const std::shared_ptr<BufferHubBase>&)>;
-
- virtual ~BufferHubQueue() {}
-
- // Creates a new consumer queue that is attached to the producer. Returns
- // a new consumer queue client or nullptr on failure.
- std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
-
- // Creates a new consumer queue that is attached to the producer. This queue
- // sets each of its imported consumer buffers to the ignored state to avoid
- // participation in lifecycle events.
- std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue();
-
- // Returns whether the buffer queue is in async mode.
- bool is_async() const { return is_async_; }
-
- // Returns the default buffer width of this buffer queue.
- uint32_t default_width() const { return default_width_; }
-
- // Returns the default buffer height of this buffer queue.
- uint32_t default_height() const { return default_height_; }
-
- // Returns the default buffer format of this buffer queue.
- uint32_t default_format() const { return default_format_; }
-
- // Creates a new consumer in handle form for immediate transport over RPC.
- pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
- bool silent = false);
-
- // Creates a new consumer in parcelable form for immediate transport over
- // Binder.
- pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable(
- bool silent = false);
-
- // Returns the number of buffers avaiable for dequeue.
- size_t count() const { return available_buffers_.size(); }
-
- // Returns the total number of buffers that the queue is tracking.
- size_t capacity() const { return capacity_; }
-
- // Returns the size of metadata structure associated with this queue.
- size_t metadata_size() const { return user_metadata_size_; }
-
- // Returns whether the buffer queue is full.
- bool is_full() const {
- return available_buffers_.size() >= kMaxQueueCapacity;
- }
-
- // Returns whether the buffer queue is connected to bufferhubd.
- bool is_connected() const { return !!GetChannel(); }
-
- int GetBufferId(size_t slot) const {
- return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id()
- : -1;
- }
-
- std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const {
- return buffers_[slot];
- }
-
- pdx::Status<int> GetEventMask(int events) {
- if (auto* client_channel = GetChannel()) {
- return client_channel->GetEventMask(events);
- } else {
- return pdx::ErrorStatus(EINVAL);
- }
- }
-
- // Returns an fd that signals pending queue events using
- // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be
- // called to handle pending queue events.
- int queue_fd() const { return epoll_fd_.Get(); }
-
- // Handles any pending events, returning available buffers to the queue and
- // reaping disconnected buffers. Returns true if successful, false if an error
- // occurred.
- bool HandleQueueEvents() { return WaitForBuffers(0); }
-
- // Set buffer event callbacks, which are std::function wrappers. The caller is
- // responsible for ensuring the validity of these callbacks' callable targets.
- void SetBufferAvailableCallback(BufferAvailableCallback callback);
- void SetBufferRemovedCallback(BufferRemovedCallback callback);
-
- // The queue tracks at most this many buffers.
- static constexpr size_t kMaxQueueCapacity =
- android::BufferQueueDefs::NUM_BUFFER_SLOTS;
-
- static constexpr int kNoTimeOut = -1;
-
- int id() const { return id_; }
- bool hung_up() const { return hung_up_; }
-
- protected:
- explicit BufferHubQueue(pdx::LocalChannelHandle channel);
- explicit BufferHubQueue(const std::string& endpoint_path);
-
- // Imports the queue parameters by querying BufferHub for the parameters for
- // this channel.
- pdx::Status<void> ImportQueue();
-
- // Sets up the queue with the given parameters.
- void SetupQueue(const QueueInfo& queue_info);
-
- // Register a buffer for management by the queue. Used by subclasses to add a
- // buffer to internal bookkeeping.
- pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer,
- size_t slot);
-
- // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only
- // to deregister a buffer for epoll and internal bookkeeping.
- virtual pdx::Status<void> RemoveBuffer(size_t slot);
-
- // Free all buffers that belongs to this queue. Can only be called from
- // producer side.
- virtual pdx::Status<void> FreeAllBuffers();
-
- // Dequeue a buffer from the free queue, blocking until one is available. The
- // timeout argument specifies the number of milliseconds that |Dequeue()| will
- // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
- // while specifying a timeout equal to zero cause Dequeue() to return
- // immediately, even if no buffers are available.
- pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout,
- size_t* slot);
-
- // Waits for buffers to become available and adds them to the available queue.
- bool WaitForBuffers(int timeout);
-
- pdx::Status<void> HandleBufferEvent(size_t slot, int event_fd,
- int poll_events);
- pdx::Status<void> HandleQueueEvent(int poll_events);
-
- // Entry in the priority queue of available buffers that stores related
- // per-buffer data.
- struct Entry {
- Entry() : slot(0) {}
- Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot,
- uint64_t in_index)
- : buffer(in_buffer), slot(in_slot), index(in_index) {}
- Entry(const std::shared_ptr<BufferHubBase>& in_buffer,
- std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence,
- size_t in_slot)
- : buffer(in_buffer),
- metadata(std::move(in_metadata)),
- fence(std::move(in_fence)),
- slot(in_slot) {}
- Entry(Entry&&) = default;
- Entry& operator=(Entry&&) = default;
-
- std::shared_ptr<BufferHubBase> buffer;
- std::unique_ptr<uint8_t[]> metadata;
- pdx::LocalHandle fence;
- size_t slot;
- uint64_t index;
- };
-
- struct EntryComparator {
- bool operator()(const Entry& lhs, const Entry& rhs) {
- return lhs.index > rhs.index;
- }
- };
-
- // Enqueues a buffer to the available list (Gained for producer or Acquireed
- // for consumer).
- pdx::Status<void> Enqueue(Entry entry);
-
- // Called when a buffer is allocated remotely.
- virtual pdx::Status<void> OnBufferAllocated() { return {}; }
-
- // Size of the metadata that buffers in this queue cary.
- size_t user_metadata_size_{0};
-
- // Buffers and related data that are available for dequeue.
- std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
- available_buffers_;
-
- // Slot of the buffers that are not available for normal dequeue. For example,
- // the slot of posted or acquired buffers in the perspective of a producer.
- std::vector<size_t> unavailable_buffers_slot_;
-
- private:
- void Initialize();
-
- // Special epoll data field indicating that the epoll event refers to the
- // queue.
- static constexpr int64_t kEpollQueueEventIndex = -1;
-
- static constexpr size_t kMaxEvents = 128;
-
- // The u64 data field of an epoll event is interpreted as int64_t:
- // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
- // element of |buffers_| as a direct index;
- static bool is_buffer_event_index(int64_t index) {
- return index >= 0 &&
- index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
- }
-
- // When |index| == kEpollQueueEventIndex it refers to the queue itself.
- static bool is_queue_event_index(int64_t index) {
- return index == BufferHubQueue::kEpollQueueEventIndex;
- }
-
- // Whether the buffer queue is operating in Async mode.
- // From GVR's perspective of view, this means a buffer can be acquired
- // asynchronously by the compositor.
- // From Android Surface's perspective of view, this is equivalent to
- // IGraphicBufferProducer's async mode. When in async mode, a producer
- // will never block even if consumer is running slow.
- bool is_async_{false};
-
- // Default buffer width that is set during ProducerQueue's creation.
- uint32_t default_width_{1};
-
- // Default buffer height that is set during ProducerQueue's creation.
- uint32_t default_height_{1};
-
- // Default buffer format that is set during ProducerQueue's creation.
- uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888
-
- // Tracks the buffers belonging to this queue. Buffers are stored according to
- // "slot" in this vector. Each slot is a logical id of the buffer within this
- // queue regardless of its queue position or presence in the ring buffer.
- std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_;
-
- // Keeps track with how many buffers have been added into the queue.
- size_t capacity_{0};
-
- // Epoll fd used to manage buffer events.
- EpollFileDescriptor epoll_fd_;
-
- // Flag indicating that the other side hung up. For ProducerQueues this
- // triggers when BufferHub dies or explicitly closes the queue channel. For
- // ConsumerQueues this can either mean the same or that the ProducerQueue on
- // the other end hung up.
- bool hung_up_{false};
-
- // Global id for the queue that is consistent across processes.
- int id_{-1};
-
- // Buffer event callbacks
- BufferAvailableCallback on_buffer_available_;
- BufferRemovedCallback on_buffer_removed_;
-
- BufferHubQueue(const BufferHubQueue&) = delete;
- void operator=(BufferHubQueue&) = delete;
-};
-
-class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
- public:
- // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
- // in |usage_clear_mask| will be automatically masked off. Note that
- // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
- // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
- // allocation through this producer queue shall not have any of the usage bits
- // in |usage_deny_set_mask| set. Allocation calls violating this will be
- // rejected. All buffer allocation through this producer queue must have all
- // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
- // this will be rejected. Note that |usage_deny_set_mask| and
- // |usage_deny_clear_mask| shall not conflict with each other. Such
- // configuration will be treated as invalid input on creation.
- static std::unique_ptr<ProducerQueue> Create(
- const ProducerQueueConfig& config, const UsagePolicy& usage) {
- return BASE::Create(config, usage);
- }
-
- // Import a ProducerQueue from a channel handle.
- static std::unique_ptr<ProducerQueue> Import(pdx::LocalChannelHandle handle) {
- return BASE::Create(std::move(handle));
- }
-
- // Get a producer buffer. Note that the method doesn't check whether the
- // buffer slot has a valid buffer that has been allocated already. When no
- // buffer has been imported before it returns nullptr; otherwise it returns
- // a shared pointer to a ProducerBuffer.
- std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const {
- return std::static_pointer_cast<ProducerBuffer>(
- BufferHubQueue::GetBuffer(slot));
- }
-
- // Batch allocate buffers. Once allocated, producer buffers are automatically
- // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED
- // state). Upon success, returns a list of slots for each buffer allocated.
- pdx::Status<std::vector<size_t>> AllocateBuffers(
- uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t buffer_count);
-
- // Allocate producer buffer to populate the queue. Once allocated, a producer
- // buffer is automatically enqueue'd into the ProducerQueue and available to
- // use (i.e. in GAINED state). Upon success, returns the slot number for the
- // buffer allocated.
- pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height,
- uint32_t layer_count, uint32_t format,
- uint64_t usage);
-
- // Add a producer buffer to populate the queue. Once added, a producer buffer
- // is available to use (i.e. in GAINED state).
- pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer,
- size_t slot);
-
- // Inserts a ProducerBuffer into the queue. On success, the method returns the
- // |slot| number where the new buffer gets inserted. Note that the buffer
- // being inserted should be in Gain'ed state prior to the call and it's
- // considered as already Dequeued when the function returns.
- pdx::Status<size_t> InsertBuffer(
- const std::shared_ptr<ProducerBuffer>& buffer);
-
- // Remove producer buffer from the queue.
- pdx::Status<void> RemoveBuffer(size_t slot) override;
-
- // Free all buffers on this producer queue.
- pdx::Status<void> FreeAllBuffers() override {
- return BufferHubQueue::FreeAllBuffers();
- }
-
- // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
- // and caller should call Post() once it's done writing to release the buffer
- // to the consumer side.
- // @return a buffer in gained state, which was originally in released state.
- pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
- int timeout, size_t* slot, pdx::LocalHandle* release_fence);
-
- // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
- // and caller should call Post() once it's done writing to release the buffer
- // to the consumer side.
- //
- // @param timeout to dequeue a buffer.
- // @param slot is the slot of the output ProducerBuffer.
- // @param release_fence for gaining a buffer.
- // @param out_meta metadata of the output buffer.
- // @param gain_posted_buffer whether to gain posted buffer if no released
- // buffer is available to gain.
- // @return a buffer in gained state, which was originally in released state if
- // gain_posted_buffer is false, or in posted/released state if
- // gain_posted_buffer is true.
- // TODO(b/112007999): gain_posted_buffer true is only used to prevent
- // libdvrtracking from starving when there are non-responding clients. This
- // gain_posted_buffer param can be removed once libdvrtracking start to use
- // the new AHardwareBuffer API.
- pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* release_fence, bool gain_posted_buffer = false);
-
- // Enqueues a producer buffer in the queue.
- pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer,
- size_t slot, uint64_t index) {
- return BufferHubQueue::Enqueue({buffer, slot, index});
- }
-
- // Takes out the current producer queue as a binder parcelable object. Note
- // that the queue must be empty to be exportable. After successful export, the
- // producer queue client should no longer be used.
- pdx::Status<ProducerQueueParcelable> TakeAsParcelable();
-
- private:
- friend BASE;
-
- // Constructors are automatically exposed through ProducerQueue::Create(...)
- // static template methods inherited from ClientBase, which take the same
- // arguments as the constructors.
- explicit ProducerQueue(pdx::LocalChannelHandle handle);
- ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
-
- // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
- // and caller should call Post() once it's done writing to release the buffer
- // to the consumer side.
- //
- // @param slot the slot of the returned buffer.
- // @return a buffer in gained state, which was originally in posted state or
- // released state.
- pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer(
- size_t* slot);
-};
-
-class ConsumerQueue : public BufferHubQueue {
- public:
- // Get a consumer buffer. Note that the method doesn't check whether the
- // buffer slot has a valid buffer that has been imported already. When no
- // buffer has been imported before it returns nullptr; otherwise returns a
- // shared pointer to a ConsumerBuffer.
- std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const {
- return std::static_pointer_cast<ConsumerBuffer>(
- BufferHubQueue::GetBuffer(slot));
- }
-
- // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls
- // whether or not buffers are set to be ignored when imported. This may be
- // used to avoid participation in the buffer lifecycle by a consumer queue
- // that is only used to spawn other consumer queues, such as in an
- // intermediate service.
- static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle);
-
- // Import newly created buffers from the service side.
- // Returns number of buffers successfully imported or an error.
- pdx::Status<size_t> ImportBuffers();
-
- // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
- // mode, and caller should call Releasse() once it's done writing to release
- // the buffer to the producer side. |meta| is passed along from BufferHub,
- // The user of ProducerBuffer is responsible with making sure that the
- // Dequeue() is done with the corect metadata type and size with those used
- // when the buffer is orignally created.
- template <typename Meta>
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) {
- return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
- }
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) {
- return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
- }
-
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, void* meta, size_t user_metadata_size,
- pdx::LocalHandle* acquire_fence);
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* acquire_fence);
-
- private:
- friend BufferHubQueue;
-
- explicit ConsumerQueue(pdx::LocalChannelHandle handle);
-
- // Add a consumer buffer to populate the queue. Once added, a consumer buffer
- // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
- // will catch the |Post| and |Acquire| the buffer to make it available for
- // consumer.
- pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- size_t slot);
-
- pdx::Status<void> OnBufferAllocated() override;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
deleted file mode 100644
index 36ab5f6..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/channel_parcelable.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-namespace android {
-namespace dvr {
-
-enum BufferHubQueueParcelableMagic : uint32_t {
- Producer = 0x62687170, // 'bhqp'
- Consumer = 0x62687163, // 'bhqc'
-};
-
-template <BufferHubQueueParcelableMagic Magic>
-class BufferHubQueueParcelable : public Parcelable {
- public:
- BufferHubQueueParcelable() = default;
-
- BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default;
- BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept {
- channel_parcelable_ = std::move(other.channel_parcelable_);
- return *this;
- }
-
- // Constructs an parcelable contains the channel parcelable.
- explicit BufferHubQueueParcelable(
- std::unique_ptr<pdx::ChannelParcelable> channel_parcelable)
- : channel_parcelable_(std::move(channel_parcelable)) {}
-
- BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete;
- void operator=(const BufferHubQueueParcelable&) = delete;
-
- bool IsValid() const;
-
- // Returns a channel handle constructed from this parcelable object and takes
- // the ownership of all resources from the parcelable object.
- pdx::LocalChannelHandle TakeChannelHandle();
-
- // Serializes the queue parcelable into the given parcel. Note that no system
- // resources are getting duplicated, nor did the parcel takes ownership of the
- // queue parcelable. Thus, the parcelable object must remain valid for the
- // lifetime of the parcel.
- status_t writeToParcel(Parcel* parcel) const override;
-
- // Deserialize the queue parcelable from the given parcel. Note that system
- // resources are duplicated from the parcel into the queue parcelable. Returns
- // error if the targeting parcelable object is already valid.
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_;
-};
-
-using ProducerQueueParcelable =
- BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>;
-using ConsumerQueueParcelable =
- BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>;
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
deleted file mode 100644
index 2f14f7c..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-
-#include <android-base/unique_fd.h>
-#include <log/log.h>
-#include <sys/epoll.h>
-
-namespace android {
-namespace dvr {
-
-class EpollFileDescriptor {
- public:
- static const int CTL_ADD = EPOLL_CTL_ADD;
- static const int CTL_MOD = EPOLL_CTL_MOD;
- static const int CTL_DEL = EPOLL_CTL_DEL;
-
- EpollFileDescriptor() : fd_(-1) {}
-
- // Constructs an EpollFileDescriptor from an integer file descriptor and
- // takes ownership.
- explicit EpollFileDescriptor(int fd) : fd_(fd) {}
-
- bool IsValid() const { return fd_.get() >= 0; }
-
- int Create() {
- if (IsValid()) {
- ALOGW("epoll fd has already been created.");
- return -EALREADY;
- }
-
- fd_.reset(epoll_create1(EPOLL_CLOEXEC));
-
- if (fd_.get() < 0)
- return -errno;
- else
- return 0;
- }
-
- int Control(int op, int target_fd, epoll_event* ev) {
- if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
- return -errno;
- else
- return 0;
- }
-
- int Wait(epoll_event* events, int maxevents, int timeout) {
- int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
-
- if (ret < 0)
- return -errno;
- else
- return ret;
- }
-
- int Get() const { return fd_.get(); }
-
- private:
- base::unique_fd fd_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
deleted file mode 100644
index 33a0d75..0000000
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ /dev/null
@@ -1,66 +0,0 @@
-
-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"],
-}
-
-header_libraries = [
- "libdvr_headers",
-]
-
-shared_libraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
-]
-
-static_libraries = [
- "libchrome",
- "libdvrcommon",
- "libperformance",
-]
-
-cc_test {
- srcs: ["buffer_hub_queue-test.cpp"],
- header_libs: header_libraries,
- static_libs: static_libraries,
- shared_libs: shared_libraries,
- cflags: [
- "-DLOG_TAG=\"buffer_hub_queue-test\"",
- "-DTRACE=0",
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
- "-Wno-error=sign-compare", // to fix later
- ],
- name: "buffer_hub_queue-test",
-}
-
-cc_test {
- srcs: ["buffer_hub_queue_producer-test.cpp"],
- header_libs: header_libraries,
- static_libs: static_libraries,
- shared_libs: shared_libraries,
- cflags: [
- "-DLOG_TAG=\"buffer_hub_queue_producer-test\"",
- "-DTRACE=0",
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
- ],
- name: "buffer_hub_queue_producer-test",
-}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
deleted file mode 100644
index 6ae603b..0000000
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ /dev/null
@@ -1,1083 +0,0 @@
-#include <base/logging.h>
-#include <binder/Parcel.h>
-#include <dvr/dvr_api.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-
-#include <gtest/gtest.h>
-#include <poll.h>
-#include <sys/eventfd.h>
-
-#include <vector>
-
-// Enable/disable debug logging.
-#define TRACE 0
-
-namespace android {
-namespace dvr {
-
-using pdx::LocalChannelHandle;
-using pdx::LocalHandle;
-
-namespace {
-
-constexpr uint32_t kBufferWidth = 100;
-constexpr uint32_t kBufferHeight = 1;
-constexpr uint32_t kBufferLayerCount = 1;
-constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
-constexpr int kTimeoutMs = 100;
-constexpr int kNoTimeout = 0;
-
-class BufferHubQueueTest : public ::testing::Test {
- public:
- bool CreateProducerQueue(const ProducerQueueConfig& config,
- const UsagePolicy& usage) {
- producer_queue_ = ProducerQueue::Create(config, usage);
- return producer_queue_ != nullptr;
- }
-
- bool CreateConsumerQueue() {
- if (producer_queue_) {
- consumer_queue_ = producer_queue_->CreateConsumerQueue();
- return consumer_queue_ != nullptr;
- } else {
- return false;
- }
- }
-
- bool CreateQueues(const ProducerQueueConfig& config,
- const UsagePolicy& usage) {
- return CreateProducerQueue(config, usage) && CreateConsumerQueue();
- }
-
- void AllocateBuffer(size_t* slot_out = nullptr) {
- // Create producer buffer.
- auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount,
- kBufferFormat, kBufferUsage);
-
- ASSERT_TRUE(status.ok());
- size_t slot = status.take();
- if (slot_out)
- *slot_out = slot;
- }
-
- bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) {
- pollfd pfd{queue->queue_fd(), POLLIN, 0};
- int ret;
- do {
- ret = poll(&pfd, 1, timeout_ms);
- } while (ret == -1 && errno == EINTR);
-
- if (ret < 0) {
- ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(),
- strerror(errno));
- return false;
- } else if (ret == 0) {
- return false;
- }
- return queue->HandleQueueEvents();
- }
-
- protected:
- ProducerQueueConfigBuilder config_builder_;
- std::unique_ptr<ProducerQueue> producer_queue_;
- std::unique_ptr<ConsumerQueue> consumer_queue_;
-};
-
-TEST_F(BufferHubQueueTest, TestDequeue) {
- const int64_t nb_dequeue_times = 16;
-
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate only one buffer.
- AllocateBuffer();
-
- // But dequeue multiple times.
- for (int64_t i = 0; i < nb_dequeue_times; i++) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer gains a buffer.
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Producer posts the buffer.
- mi.index = i;
- EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-
- // Consumer acquires a buffer.
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
- EXPECT_EQ(mi.index, i);
- EXPECT_EQ(mo.index, i);
-
- // Consumer releases the buffer.
- EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
- }
-}
-
-TEST_F(BufferHubQueueTest,
- TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate 3 buffers to use.
- const size_t test_queue_capacity = 3;
- for (int64_t i = 0; i < test_queue_capacity; i++) {
- AllocateBuffer();
- }
- EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
- size_t producer_slot, consumer_slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer posts 2 buffers and remember their posted sequence.
- std::deque<size_t> posted_slots;
- for (int64_t i = 0; i < 2; i++) {
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Producer should not be gaining posted buffer when there are still
- // available buffers to gain.
- auto found_iter =
- std::find(posted_slots.begin(), posted_slots.end(), producer_slot);
- EXPECT_EQ(found_iter, posted_slots.end());
- posted_slots.push_back(producer_slot);
-
- // Producer posts the buffer.
- mi.index = i;
- EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
- }
-
- // Consumer acquires one buffer.
- auto c1_status =
- consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
- // Consumer should get the oldest posted buffer. No checks here.
- // posted_slots[0] should be in acquired state now.
- EXPECT_EQ(mo.index, 0);
- // Consumer releases the buffer.
- EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
- // posted_slots[0] should be in released state now.
-
- // Producer gain and post 2 buffers.
- for (int64_t i = 0; i < 2; i++) {
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // The gained buffer should be the one in released state or the one haven't
- // been use.
- EXPECT_NE(posted_slots[1], producer_slot);
-
- mi.index = i + 2;
- EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
- }
-
- // Producer gains a buffer.
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // The gained buffer should be the oldest posted buffer.
- EXPECT_EQ(posted_slots[1], producer_slot);
-
- // Producer posts the buffer.
- mi.index = 4;
- EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-}
-
-TEST_F(BufferHubQueueTest,
- TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate 4 buffers to use.
- const size_t test_queue_capacity = 4;
- for (int64_t i = 0; i < test_queue_capacity; i++) {
- AllocateBuffer();
- }
- EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
- // Post all allowed buffers and remember their posted sequence.
- std::deque<size_t> posted_slots;
- for (int64_t i = 0; i < test_queue_capacity; i++) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer gains a buffer.
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Producer should not be gaining posted buffer when there are still
- // available buffers to gain.
- auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot);
- EXPECT_EQ(found_iter, posted_slots.end());
- posted_slots.push_back(slot);
-
- // Producer posts the buffer.
- mi.index = i;
- EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
- }
-
- // Gain posted buffers in sequence.
- const int64_t nb_dequeue_all_times = 2;
- for (int j = 0; j < nb_dequeue_all_times; ++j) {
- for (int i = 0; i < test_queue_capacity; ++i) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer gains a buffer.
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // The gained buffer should be the oldest posted buffer.
- EXPECT_EQ(posted_slots[i], slot);
-
- // Producer posts the buffer.
- mi.index = i + test_queue_capacity * (j + 1);
- EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
- }
- }
-}
-
-TEST_F(BufferHubQueueTest, TestProducerConsumer) {
- const size_t kBufferCount = 16;
- size_t slot;
- DvrNativeBufferMetadata mi, mo;
- LocalHandle fence;
-
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- for (size_t i = 0; i < kBufferCount; i++) {
- AllocateBuffer();
-
- // Producer queue has all the available buffers on initialize.
- ASSERT_EQ(producer_queue_->count(), i + 1);
- ASSERT_EQ(producer_queue_->capacity(), i + 1);
-
- // Consumer queue has no avaiable buffer on initialize.
- ASSERT_EQ(consumer_queue_->count(), 0U);
- // Consumer queue does not import buffers until a dequeue is issued.
- ASSERT_EQ(consumer_queue_->capacity(), i);
- // Dequeue returns timeout since no buffer is ready to consumer, but
- // this implicitly triggers buffer import and bump up |capacity|.
- auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
- ASSERT_FALSE(status.ok());
- ASSERT_EQ(ETIMEDOUT, status.error());
- ASSERT_EQ(consumer_queue_->capacity(), i + 1);
- }
-
- // Use eventfd as a stand-in for a fence.
- LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-
- for (size_t i = 0; i < kBufferCount; i++) {
- // First time there is no buffer available to dequeue.
- auto consumer_status =
- consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
- ASSERT_FALSE(consumer_status.ok());
- ASSERT_EQ(consumer_status.error(), ETIMEDOUT);
-
- // Make sure Producer buffer is POSTED so that it's ready to Accquire
- // in the consumer's Dequeue() function.
- auto producer_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- auto producer = producer_status.take();
- ASSERT_NE(nullptr, producer);
-
- mi.index = static_cast<int64_t>(i);
- ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0);
-
- // Second time the just the POSTED buffer should be dequeued.
- consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok());
- EXPECT_TRUE(fence.IsValid());
-
- auto consumer = consumer_status.take();
- ASSERT_NE(nullptr, consumer);
- ASSERT_EQ(mi.index, mo.index);
- }
-}
-
-TEST_F(BufferHubQueueTest, TestInsertBuffer) {
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
- consumer_queue_ = producer_queue_->CreateConsumerQueue();
- ASSERT_TRUE(consumer_queue_ != nullptr);
- EXPECT_EQ(producer_queue_->capacity(), 0);
- EXPECT_EQ(consumer_queue_->capacity(), 0);
-
- std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
- kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
- ASSERT_TRUE(p1 != nullptr);
- ASSERT_EQ(p1->GainAsync(), 0);
-
- // Inserting a posted buffer will fail.
- DvrNativeBufferMetadata meta;
- EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
- auto status_or_slot = producer_queue_->InsertBuffer(p1);
- EXPECT_FALSE(status_or_slot.ok());
- EXPECT_EQ(status_or_slot.error(), EINVAL);
-
- // Inserting a gained buffer will succeed.
- std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create(
- kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
- ASSERT_EQ(p2->GainAsync(), 0);
- ASSERT_TRUE(p2 != nullptr);
- status_or_slot = producer_queue_->InsertBuffer(p2);
- EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
- // This is the first buffer inserted, should take slot 0.
- size_t slot = status_or_slot.get();
- EXPECT_EQ(slot, 0);
-
- // Wait and expect the consumer to kick up the newly inserted buffer.
- WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
- EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
-}
-
-TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
- DvrNativeBufferMetadata mo;
-
- // Allocate buffers.
- const size_t kBufferCount = 4u;
- for (size_t i = 0; i < kBufferCount; i++) {
- AllocateBuffer();
- }
- ASSERT_EQ(kBufferCount, producer_queue_->count());
- ASSERT_EQ(kBufferCount, producer_queue_->capacity());
-
- consumer_queue_ = producer_queue_->CreateConsumerQueue();
- ASSERT_NE(nullptr, consumer_queue_);
-
- // Check that buffers are correctly imported on construction.
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_EQ(0u, consumer_queue_->count());
-
- // Dequeue all the buffers and keep track of them in an array. This prevents
- // the producer queue ring buffer ref counts from interfering with the tests.
- struct Entry {
- std::shared_ptr<ProducerBuffer> buffer;
- LocalHandle fence;
- size_t slot;
- };
- std::array<Entry, kBufferCount> buffers;
-
- for (size_t i = 0; i < kBufferCount; i++) {
- Entry* entry = &buffers[i];
- auto producer_status =
- producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence);
- ASSERT_TRUE(producer_status.ok());
- entry->buffer = producer_status.take();
- ASSERT_NE(nullptr, entry->buffer);
- }
-
- // Remove a buffer and make sure both queues reflect the change.
- ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot));
- EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
- // As long as the removed buffer is still alive the consumer queue won't know
- // its gone.
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Release the removed buffer.
- buffers[0].buffer = nullptr;
-
- // Now the consumer queue should know it's gone.
- EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs));
- ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity());
-
- // Allocate a new buffer. This should take the first empty slot.
- size_t slot;
- AllocateBuffer(&slot);
- ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
- EXPECT_EQ(buffers[0].slot, slot);
- EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
- // The consumer queue should pick up the new buffer.
- EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Remove and allocate a buffer.
- ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot));
- EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
- buffers[1].buffer = nullptr;
-
- AllocateBuffer(&slot);
- ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
- EXPECT_EQ(buffers[1].slot, slot);
- EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
- // The consumer queue should pick up the new buffer but the count shouldn't
- // change.
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Remove and allocate a buffer, but don't free the buffer right away.
- ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot));
- EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
- AllocateBuffer(&slot);
- ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
- EXPECT_EQ(buffers[2].slot, slot);
- EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Release the producer buffer to trigger a POLLHUP event for an already
- // removed buffer.
- buffers[2].buffer = nullptr;
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-}
-
-TEST_F(BufferHubQueueTest, TestMultipleConsumers) {
- // ProducerConfigureBuilder doesn't set Metadata{size}, which means there
- // is no metadata associated with this BufferQueue's buffer.
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate buffers.
- const size_t kBufferCount = 4u;
- for (size_t i = 0; i < kBufferCount; i++) {
- AllocateBuffer();
- }
- ASSERT_EQ(kBufferCount, producer_queue_->count());
-
- // Build a silent consumer queue to test multi-consumer queue features.
- auto silent_queue = producer_queue_->CreateSilentConsumerQueue();
- ASSERT_NE(nullptr, silent_queue);
-
- // Check that silent queue doesn't import buffers on creation.
- EXPECT_EQ(silent_queue->capacity(), 0U);
-
- // Dequeue and post a buffer.
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
- auto producer_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(producer_status.ok());
- auto producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
- // After post, check the number of remaining available buffers.
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
- // Currently we expect no buffer to be available prior to calling
- // WaitForBuffers/HandleQueueEvents.
- // TODO(eieio): Note this behavior may change in the future.
- EXPECT_EQ(silent_queue->count(), 0U);
- EXPECT_FALSE(silent_queue->HandleQueueEvents());
- EXPECT_EQ(silent_queue->count(), 0U);
-
- // Build a new consumer queue to test multi-consumer queue features.
- consumer_queue_ = silent_queue->CreateConsumerQueue();
- ASSERT_NE(consumer_queue_, nullptr);
-
- // Check that buffers are correctly imported on construction.
- EXPECT_EQ(consumer_queue_->capacity(), kBufferCount);
- // Buffers are only imported, but their availability is not checked until
- // first call to Dequeue().
- EXPECT_EQ(consumer_queue_->count(), 0U);
-
- // Reclaim released/ignored buffers.
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
- usleep(10000);
- WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
- // Post another buffer.
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
-
- // Verify that the consumer queue receives it.
- size_t consumer_queue_count = consumer_queue_->count();
- WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
- EXPECT_GT(consumer_queue_->count(), consumer_queue_count);
-
- // Save the current consumer queue buffer count to compare after the dequeue.
- consumer_queue_count = consumer_queue_->count();
-
- // Dequeue and acquire/release (discard) buffers on the consumer end.
- auto consumer_status =
- consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(consumer_status.ok());
- auto consumer_buffer = consumer_status.take();
- ASSERT_NE(consumer_buffer, nullptr);
- consumer_buffer->Discard();
-
- // Buffer should be returned to the producer queue without being handled by
- // the silent consumer queue.
- EXPECT_LT(consumer_queue_->count(), consumer_queue_count);
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 2);
-
- WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-}
-
-struct TestUserMetadata {
- char a;
- int32_t b;
- int64_t c;
-};
-
-constexpr uint64_t kUserMetadataSize =
- static_cast<uint64_t>(sizeof(TestUserMetadata));
-
-TEST_F(BufferHubQueueTest, TestUserMetadata) {
- ASSERT_TRUE(CreateQueues(
- config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
- AllocateBuffer();
-
- std::vector<TestUserMetadata> user_metadata_list = {
- {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
-
- for (auto user_metadata : user_metadata_list) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // TODO(b/69469185): Test against metadata from consumer once we implement
- // release metadata properly.
- // EXPECT_EQ(mo.user_metadata_ptr, 0U);
- // EXPECT_EQ(mo.user_metadata_size, 0U);
-
- mi.user_metadata_size = kUserMetadataSize;
- mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
- EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
-
- EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize);
- auto out_user_metadata =
- reinterpret_cast<TestUserMetadata*>(mo.user_metadata_ptr);
- EXPECT_EQ(user_metadata.a, out_user_metadata->a);
- EXPECT_EQ(user_metadata.b, out_user_metadata->b);
- EXPECT_EQ(user_metadata.c, out_user_metadata->c);
-
- // When release, empty metadata is also legit.
- mi.user_metadata_size = 0U;
- mi.user_metadata_ptr = 0U;
- c1->ReleaseAsync(&mi, {});
- }
-}
-
-TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) {
- ASSERT_TRUE(CreateQueues(
- config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
- AllocateBuffer();
-
- TestUserMetadata user_metadata;
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Post with mismatched user metadata size will fail. But the producer buffer
- // itself should stay untouched.
- mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
- mi.user_metadata_size = kUserMetadataSize + 1;
- EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG);
- // Post with the exact same user metdata size can success.
- mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
- mi.user_metadata_size = kUserMetadataSize;
- EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestEnqueue) {
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
- UsagePolicy{}));
- AllocateBuffer();
-
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(nullptr, p1);
-
- producer_queue_->Enqueue(p1, slot, 0ULL);
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_FALSE(c1_status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- size_t ps1;
- AllocateBuffer();
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // producer queue is exhausted
- size_t ps2;
- auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
- ASSERT_FALSE(p2_status.ok());
- ASSERT_EQ(ETIMEDOUT, p2_status.error());
-
- // dynamically add buffer.
- AllocateBuffer();
- ASSERT_EQ(producer_queue_->count(), 1U);
- ASSERT_EQ(producer_queue_->capacity(), 2U);
-
- // now we can dequeue again
- p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
- ASSERT_TRUE(p2_status.ok());
- auto p2 = p2_status.take();
- ASSERT_NE(p2, nullptr);
- ASSERT_EQ(producer_queue_->count(), 0U);
- // p1 and p2 should have different slot number
- ASSERT_NE(ps1, ps2);
-
- // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
- // are called. So far consumer_queue_ should be empty.
- ASSERT_EQ(consumer_queue_->count(), 0U);
-
- int64_t seq = 1;
- mi.index = seq;
- ASSERT_EQ(p1->PostAsync(&mi, {}), 0);
-
- size_t cs1, cs2;
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
- ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
- ASSERT_EQ(consumer_queue_->count(), 0U);
- ASSERT_EQ(consumer_queue_->capacity(), 2U);
- ASSERT_EQ(cs1, ps1);
-
- ASSERT_EQ(p2->PostAsync(&mi, {}), 0);
- auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence);
- ASSERT_TRUE(c2_status.ok());
- auto c2 = c2_status.take();
- ASSERT_NE(c2, nullptr);
- ASSERT_EQ(cs2, ps2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
- ASSERT_EQ(producer_queue_->capacity(), 0);
- auto status = producer_queue_->AllocateBuffers(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage, /*buffer_count=*/2);
- ASSERT_TRUE(status.ok());
- std::vector<size_t> buffer_slots = status.take();
- ASSERT_EQ(buffer_slots.size(), 2);
- ASSERT_EQ(producer_queue_->capacity(), 2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
- ASSERT_EQ(producer_queue_->capacity(), 0);
- auto status = producer_queue_->AllocateBuffers(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage, /*buffer_count=*/0);
- ASSERT_TRUE(status.ok());
- std::vector<size_t> buffer_slots = status.take();
- ASSERT_EQ(buffer_slots.size(), 0);
- ASSERT_EQ(producer_queue_->capacity(), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageSetMask) {
- const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(
- CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0}));
-
- // When allocation, leave out |set_mask| from usage bits on purpose.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage & ~set_mask);
- ASSERT_TRUE(status.ok());
-
- LocalHandle fence;
- size_t slot;
- DvrNativeBufferMetadata mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_EQ(p1->usage() & set_mask, set_mask);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageClearMask) {
- const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(
- CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0}));
-
- // When allocation, add |clear_mask| into usage bits on purpose.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage | clear_mask);
- ASSERT_TRUE(status.ok());
-
- LocalHandle fence;
- size_t slot;
- DvrNativeBufferMetadata mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_EQ(p1->usage() & clear_mask, 0U);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
- const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
- UsagePolicy{0, 0, deny_set_mask, 0}));
-
- // Now that |deny_set_mask| is illegal, allocation without those bits should
- // be able to succeed.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage & ~deny_set_mask);
- ASSERT_TRUE(status.ok());
-
- // While allocation with those bits should fail.
- status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount, kBufferFormat,
- kBufferUsage | deny_set_mask);
- ASSERT_FALSE(status.ok());
- ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
- const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
- UsagePolicy{0, 0, 0, deny_clear_mask}));
-
- // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
- // mandatory), allocation with those bits should be able to succeed.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage | deny_clear_mask);
- ASSERT_TRUE(status.ok());
-
- // While allocation without those bits should fail.
- status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount, kBufferFormat,
- kBufferUsage & ~deny_clear_mask);
- ASSERT_FALSE(status.ok());
- ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestQueueInfo) {
- static const bool kIsAsync = true;
- ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync)
- .SetDefaultWidth(kBufferWidth)
- .SetDefaultHeight(kBufferHeight)
- .SetDefaultFormat(kBufferFormat)
- .Build(),
- UsagePolicy{}));
-
- EXPECT_EQ(producer_queue_->default_width(), kBufferWidth);
- EXPECT_EQ(producer_queue_->default_height(), kBufferHeight);
- EXPECT_EQ(producer_queue_->default_format(), kBufferFormat);
- EXPECT_EQ(producer_queue_->is_async(), kIsAsync);
-
- EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth);
- EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight);
- EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat);
- EXPECT_EQ(consumer_queue_->is_async(), kIsAsync);
-}
-
-TEST_F(BufferHubQueueTest, TestFreeAllBuffers) {
- constexpr size_t kBufferCount = 2;
-
-#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers) \
- EXPECT_EQ(consumer_queue_->count(), 0U); \
- EXPECT_EQ(consumer_queue_->capacity(), 0U); \
- EXPECT_EQ(producer_queue_->count(), 0U); \
- EXPECT_EQ(producer_queue_->capacity(), 0U); \
- for (size_t i = 0; i < num_buffers; i++) { \
- AllocateBuffer(); \
- } \
- EXPECT_EQ(producer_queue_->count(), num_buffers); \
- EXPECT_EQ(producer_queue_->capacity(), num_buffers);
-
- size_t slot;
- LocalHandle fence;
- pdx::Status<void> status;
- pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status;
- pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status;
- std::shared_ptr<ConsumerBuffer> consumer_buffer;
- std::shared_ptr<ProducerBuffer> producer_buffer;
- DvrNativeBufferMetadata mi, mo;
-
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Free all buffers when buffers are avaible for dequeue.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when one buffer is dequeued.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when all buffers are dequeued.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- for (size_t i = 0; i < kBufferCount; i++) {
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- }
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when one buffer is posted.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(nullptr, producer_buffer);
- ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence));
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when all buffers are posted.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- for (size_t i = 0; i < kBufferCount; i++) {
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
- }
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when all buffers are acquired.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- for (size_t i = 0; i < kBufferCount; i++) {
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
- consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
- }
-
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // In addition to FreeAllBuffers() from the queue, it is also required to
- // delete all references to the ProducerBuffer (i.e. the PDX client).
- producer_buffer = nullptr;
-
- // Crank consumer queue events to pickup EPOLLHUP events on the queue.
- consumer_queue_->HandleQueueEvents();
-
- // One last check.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-
-#undef CHECK_NO_BUFFER_THEN_ALLOCATE
-}
-
-TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) {
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(),
- UsagePolicy{}));
-
- // Allocate only one buffer.
- AllocateBuffer();
-
- // Export should fail as the queue is not empty.
- auto status = producer_queue_->TakeAsParcelable();
- EXPECT_FALSE(status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- auto s1 = producer_queue_->TakeAsParcelable();
- EXPECT_TRUE(s1.ok());
-
- ProducerQueueParcelable output_parcelable = s1.take();
- EXPECT_TRUE(output_parcelable.IsValid());
-
- Parcel parcel;
- status_t res;
- res = output_parcelable.writeToParcel(&parcel);
- EXPECT_EQ(res, OK);
-
- // After written into parcelable, the output_parcelable is still valid has
- // keeps the producer channel alive.
- EXPECT_TRUE(output_parcelable.IsValid());
-
- // Creating producer buffer should fail.
- auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount, kBufferFormat,
- kBufferUsage);
- ASSERT_FALSE(s2.ok());
-
- // Reset the data position so that we can read back from the same parcel
- // without doing actually Binder IPC.
- parcel.setDataPosition(0);
- producer_queue_ = nullptr;
-
- // Recreate the producer queue from the parcel.
- ProducerQueueParcelable input_parcelable;
- EXPECT_FALSE(input_parcelable.IsValid());
-
- res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, OK);
- EXPECT_TRUE(input_parcelable.IsValid());
-
- EXPECT_EQ(producer_queue_, nullptr);
- producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle());
- EXPECT_FALSE(input_parcelable.IsValid());
- ASSERT_NE(producer_queue_, nullptr);
-
- // Newly created queue from the parcel can allocate buffer, post buffer to
- // consumer.
- EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
- EXPECT_EQ(producer_queue_->count(), 1U);
- EXPECT_EQ(producer_queue_->capacity(), 1U);
-
- size_t slot;
- DvrNativeBufferMetadata producer_meta;
- DvrNativeBufferMetadata consumer_meta;
- LocalHandle fence;
- auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
- EXPECT_TRUE(s3.ok());
-
- std::shared_ptr<ProducerBuffer> p1 = s3.take();
- ASSERT_NE(p1, nullptr);
-
- producer_meta.timestamp = 42;
- EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
- // Make sure the buffer can be dequeued from consumer side.
- auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
- EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
- auto consumer = s4.take();
- ASSERT_NE(consumer, nullptr);
- EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) {
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
- auto s1 = producer_queue_->CreateConsumerQueueParcelable();
- EXPECT_TRUE(s1.ok());
- ConsumerQueueParcelable output_parcelable = s1.take();
- EXPECT_TRUE(output_parcelable.IsValid());
-
- // Write to a Parcel new object.
- Parcel parcel;
- status_t res;
- res = output_parcelable.writeToParcel(&parcel);
-
- // Reset the data position so that we can read back from the same parcel
- // without doing actually Binder IPC.
- parcel.setDataPosition(0);
-
- // No consumer queue created yet.
- EXPECT_EQ(consumer_queue_, nullptr);
-
- // If the parcel contains a consumer queue, read into a
- // ProducerQueueParcelable should fail.
- ProducerQueueParcelable wrongly_typed_parcelable;
- EXPECT_FALSE(wrongly_typed_parcelable.IsValid());
- res = wrongly_typed_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, -EINVAL);
- parcel.setDataPosition(0);
-
- // Create the consumer queue from the parcel.
- ConsumerQueueParcelable input_parcelable;
- EXPECT_FALSE(input_parcelable.IsValid());
-
- res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, OK);
- EXPECT_TRUE(input_parcelable.IsValid());
-
- consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
- EXPECT_FALSE(input_parcelable.IsValid());
- ASSERT_NE(consumer_queue_, nullptr);
-
- EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
- EXPECT_EQ(producer_queue_->count(), 1U);
- EXPECT_EQ(producer_queue_->capacity(), 1U);
-
- size_t slot;
- DvrNativeBufferMetadata producer_meta;
- DvrNativeBufferMetadata consumer_meta;
- LocalHandle fence;
- auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
- EXPECT_TRUE(s2.ok());
-
- std::shared_ptr<ProducerBuffer> p1 = s2.take();
- ASSERT_NE(p1, nullptr);
-
- producer_meta.timestamp = 42;
- EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
- // Make sure the buffer can be dequeued from consumer side.
- auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
- EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
- auto consumer = s3.take();
- ASSERT_NE(consumer, nullptr);
- EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
deleted file mode 100644
index fab1097..0000000
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-#include <base/logging.h>
-#include <gui/BufferHubProducer.h>
-#include <gui/IProducerListener.h>
-#include <gui/Surface.h>
-#include <pdx/default_transport/channel_parcelable.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace dvr {
-
-using pdx::LocalHandle;
-
-namespace {
-
-// Default dimensions before setDefaultBufferSize is called by the consumer.
-constexpr uint32_t kDefaultWidth = 1;
-constexpr uint32_t kDefaultHeight = 1;
-
-// Default format before setDefaultBufferFormat is called by the consumer.
-constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-constexpr int kDefaultConsumerUsageBits = 0;
-
-// Default transform hint before setTransformHint is called by the consumer.
-constexpr uint32_t kDefaultTransformHint = 0;
-
-constexpr int kTestApi = NATIVE_WINDOW_API_CPU;
-constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL;
-constexpr int kTestApiInvalid = 0xDEADBEEF;
-constexpr int kTestProducerUsageBits = 0;
-constexpr bool kTestControlledByApp = true;
-
-// Builder pattern to slightly vary *almost* correct input
-// -- avoids copying and pasting
-struct QueueBufferInputBuilder {
- IGraphicBufferProducer::QueueBufferInput build() {
- return IGraphicBufferProducer::QueueBufferInput(
- mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode,
- mTransform, mFence);
- }
-
- QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
- this->mTimestamp = timestamp;
- return *this;
- }
-
- QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) {
- this->mIsAutoTimestamp = isAutoTimestamp;
- return *this;
- }
-
- QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) {
- this->mDataSpace = dataSpace;
- return *this;
- }
-
- QueueBufferInputBuilder& setCrop(Rect crop) {
- this->mCrop = crop;
- return *this;
- }
-
- QueueBufferInputBuilder& setScalingMode(int scalingMode) {
- this->mScalingMode = scalingMode;
- return *this;
- }
-
- QueueBufferInputBuilder& setTransform(uint32_t transform) {
- this->mTransform = transform;
- return *this;
- }
-
- QueueBufferInputBuilder& setFence(sp<Fence> fence) {
- this->mFence = fence;
- return *this;
- }
-
- private:
- int64_t mTimestamp{1384888611};
- bool mIsAutoTimestamp{false};
- android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN};
- Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)};
- int mScalingMode{0};
- uint32_t mTransform{0};
- sp<Fence> mFence{Fence::NO_FENCE};
-};
-
-// This is a test that covers our implementation of bufferhubqueue-based
-// IGraphicBufferProducer.
-class BufferHubQueueProducerTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- const ::testing::TestInfo* const testInfo =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(),
- testInfo->name());
-
- auto config = ProducerQueueConfigBuilder().Build();
- auto queue = ProducerQueue::Create(config, UsagePolicy{});
- ASSERT_TRUE(queue != nullptr);
-
- mProducer = BufferHubProducer::Create(std::move(queue));
- ASSERT_TRUE(mProducer != nullptr);
- mSurface = new Surface(mProducer, true);
- ASSERT_TRUE(mSurface != nullptr);
- }
-
- // Connect to a producer in a 'correct' fashion.
- void ConnectProducer() {
- IGraphicBufferProducer::QueueBufferOutput output;
- // Can connect the first time.
- ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi,
- kTestControlledByApp, &output));
- }
-
- // Dequeue a buffer in a 'correct' fashion.
- // Precondition: Producer is connected.
- void DequeueBuffer(int* outSlot) {
- sp<Fence> fence;
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence));
- }
-
- void DequeueBuffer(int* outSlot, sp<Fence>* outFence) {
- ASSERT_NE(nullptr, outSlot);
- ASSERT_NE(nullptr, outFence);
-
- int ret = mProducer->dequeueBuffer(
- outSlot, outFence, kDefaultWidth, kDefaultHeight, kDefaultFormat,
- kTestProducerUsageBits, nullptr, nullptr);
- // BUFFER_NEEDS_REALLOCATION can be either on or off.
- ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret);
-
- // Slot number should be in boundary.
- ASSERT_LE(0, *outSlot);
- ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot);
- }
-
- // Create a generic "valid" input for queueBuffer
- // -- uses the default buffer format, width, etc.
- static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() {
- return QueueBufferInputBuilder().build();
- }
-
- const sp<IProducerListener> kStubListener{new StubProducerListener};
-
- sp<BufferHubProducer> mProducer;
- sp<Surface> mSurface;
-};
-
-TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) {
- IGraphicBufferProducer::QueueBufferOutput output;
-
- // NULL output returns BAD_VALUE
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
- kTestControlledByApp, nullptr));
-
- // Invalid API returns bad value
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid,
- kTestControlledByApp, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // Can't connect when there is already a producer connected.
- IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi,
- kTestControlledByApp, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
-}
-
-TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // Must disconnect with same API number
- EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther));
- // API must not be out of range
- EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid));
-}
-
-TEST_F(BufferHubQueueProducerTest, Query_Succeeds) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- int32_t value = -1;
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
- EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
- EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
- EXPECT_EQ(kDefaultFormat, value);
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
- EXPECT_LE(0, value);
- EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
-
- EXPECT_EQ(OK,
- mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
- EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
-
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
- EXPECT_EQ(kDefaultConsumerUsageBits, value);
-}
-
-TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // One past the end of the last 'query' enum value. Update this if we add more
- // enums.
- const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1;
-
- int value;
- // What was out of range
- EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value));
- EXPECT_EQ(BAD_VALUE,
- mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value));
-
- // Some enums from window.h are 'invalid'
- EXPECT_EQ(BAD_VALUE,
- mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value));
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
-
- // Value was NULL
- EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL));
-}
-
-TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- // Request the buffer (pre-requisite for queueing)
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- // A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- // Queue the buffer back into the BQ
- ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-
- EXPECT_EQ(kDefaultWidth, output.width);
- EXPECT_EQ(kDefaultHeight, output.height);
- EXPECT_EQ(kDefaultTransformHint, output.transformHint);
-
- // BufferHubQueue delivers buffers to consumer immediately.
- EXPECT_EQ(0u, output.numPendingBuffers);
-
- // Note that BufferHubQueue doesn't support nextFrameNumber as it seems to
- // be a SurfaceFlinger specific optimization.
- EXPECT_EQ(0u, output.nextFrameNumber);
-
- // Buffer was not in the dequeued state
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test invalid slot number
-TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- // A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output));
- EXPECT_EQ(BAD_VALUE,
- mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output));
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS,
- input, &output));
-}
-
-// Slot was not in the dequeued state (all slots start out in Free state)
-TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output));
-}
-
-// Slot was enqueued without requesting a buffer
-TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test when fence was NULL
-TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- sp<Fence> nullFence = NULL;
-
- IGraphicBufferProducer::QueueBufferInput input =
- QueueBufferInputBuilder().setFence(nullFence).build();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test scaling mode was invalid
-TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- IGraphicBufferProducer::QueueBufferInput input =
- QueueBufferInputBuilder().setScalingMode(-1).build();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-
- input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-// Test crop rect is out of bounds of the buffer dimensions
-TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) {
- int slot = -1;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- IGraphicBufferProducer::QueueBufferInput input =
- QueueBufferInputBuilder()
- .setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1))
- .build();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) {
- int slot = -1;
- sp<Fence> fence;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
-
- // Should be able to cancel buffer after a dequeue.
- EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
-}
-
-TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
- return;
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- int minUndequeuedBuffers;
- ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
-
- const int minBuffers = 1;
- const int maxBuffers =
- BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
-
- ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers))
- << "bufferCount: " << minBuffers;
-
- // Should now be able to dequeue up to minBuffers times
- // Should now be able to dequeue up to maxBuffers times
- int slot = -1;
- for (int i = 0; i < minBuffers; ++i) {
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- }
-
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers));
-
- // queue the first buffer to enable max dequeued buffer count checking
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
- sp<GraphicBuffer> buffer;
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
- ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
-
- sp<Fence> fence;
- for (int i = 0; i < maxBuffers; ++i) {
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
- }
-
- // Cancel a buffer, so we can decrease the buffer count
- ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence));
-
- // Should now be able to decrease the max dequeued count by 1
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
-}
-
-TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
-
- int minUndequeuedBuffers;
- ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
-
- const int minBuffers = 1;
- const int maxBuffers =
- BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
-
- ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
- // Buffer count was out of range
- EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
- << "bufferCount: " << 0;
- EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1))
- << "bufferCount: " << maxBuffers + 1;
-
- // Set max dequeue count to 2
- ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
- // Dequeue 2 buffers
- int slot = -1;
- sp<Fence> fence;
- for (int i = 0; i < 2; i++) {
- ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
- (mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
- kDefaultHeight, kDefaultFormat,
- kTestProducerUsageBits,
- nullptr, nullptr)))
- << "slot: " << slot;
- }
-
- // Client has too many buffers dequeued
- EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1))
- << "bufferCount: " << minBuffers;
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_dequeueBuffer) {
- int slot = -1;
- sp<Fence> fence;
-
- ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
- kDefaultHeight, kDefaultFormat,
- kTestProducerUsageBits,
- nullptr, nullptr));
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_requestBuffer) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
-
- // Shouldn't be able to request buffer after disconnect.
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
- ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_queueBuffer) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- // A generic "valid" input
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- // Shouldn't be able to queue buffer after disconnect.
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
- ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
-}
-
-TEST_F(BufferHubQueueProducerTest,
- DisconnectedProducerReturnsError_cancelBuffer) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
-
- ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
-
- // Shouldn't be able to cancel buffer after disconnect.
- ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
- ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
-}
-
-TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) {
- int slot = -1;
- sp<GraphicBuffer> buffer;
- IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
- IGraphicBufferProducer::QueueBufferOutput output;
-
- EXPECT_NO_FATAL_FAILURE(ConnectProducer());
-
- constexpr int maxDequeuedBuffers = 1;
- int minUndequeuedBuffers;
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
- EXPECT_EQ(OK, mProducer->setAsyncMode(false));
- EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
-
- int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
-
- // Dequeue, request, and queue all buffers.
- for (int i = 0; i < maxCapacity; i++) {
- EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- }
-
- // Disconnect then reconnect.
- EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
- EXPECT_NO_FATAL_FAILURE(ConnectProducer());
-
- // Dequeue, request, and queue all buffers.
- for (int i = 0; i < maxCapacity; i++) {
- EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
- }
-
- EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
-}
-
-TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) {
- // Connected producer cannot be taken out as a parcelable.
- EXPECT_NO_FATAL_FAILURE(ConnectProducer());
- ProducerQueueParcelable producer_parcelable;
- EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE);
-
- // Create a valid fake producer parcelable.
- auto fake_channel_parcelable =
- std::make_unique<pdx::default_transport::ChannelParcelable>(
- LocalHandle(0), LocalHandle(0), LocalHandle(0));
- EXPECT_TRUE(fake_channel_parcelable->IsValid());
- ProducerQueueParcelable fake_producer_parcelable(
- std::move(fake_channel_parcelable));
- EXPECT_TRUE(fake_producer_parcelable.IsValid());
-
- // Disconnect producer can be taken out, but only to an invalid parcelable.
- ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
- EXPECT_EQ(mProducer->TakeAsParcelable(&fake_producer_parcelable), BAD_VALUE);
- EXPECT_FALSE(producer_parcelable.IsValid());
- EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
- EXPECT_TRUE(producer_parcelable.IsValid());
-
- // Should still be able to query buffer dimension after disconnect.
- int32_t value = -1;
- EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
- EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth);
-
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK);
- EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight);
-
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK);
- EXPECT_EQ(value, kDefaultFormat);
-
- // But connect to API will fail.
- IGraphicBufferProducer::QueueBufferOutput output;
- EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp,
- &output),
- BAD_VALUE);
-
- // Create a new producer from the parcelable and connect to kTestApi should
- // succeed.
- sp<BufferHubProducer> new_producer =
- BufferHubProducer::Create(std::move(producer_parcelable));
- ASSERT_TRUE(new_producer != nullptr);
- EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp,
- &output),
- OK);
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
deleted file mode 100644
index b0ed950..0000000
--- a/libs/vr/libdisplay/Android.bp
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2015 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"],
-}
-
-sourceFiles = [
- "display_client.cpp",
- "display_manager_client.cpp",
- "display_protocol.cpp",
- "shared_buffer_helpers.cpp",
- "vsync_service.cpp",
-]
-
-localIncludeFiles = [
- "include",
-]
-
-sharedLibraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "liblog",
- "libutils",
- "libui",
- "libgui",
- "libhardware",
- "libsync",
- "libnativewindow",
- "libpdx_default_transport",
-]
-
-staticLibraries = [
- "libdvrcommon",
- "libbroadcastring",
-]
-
-headerLibraries = [
- "vulkan_headers",
- "libdvr_headers",
-]
-
-cc_library {
- srcs: sourceFiles,
- cflags: ["-DLOG_TAG=\"libdisplay\"",
- "-DTRACE=0",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-DGL_GLEXT_PROTOTYPES",
- "-DEGL_EGLEXT_PROTOTYPES",
- "-Wall",
- "-Werror",
- ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
- export_include_dirs: localIncludeFiles,
- shared_libs: sharedLibraries,
- static_libs: staticLibraries,
- header_libs: headerLibraries,
- export_header_lib_headers: headerLibraries,
-
- name: "libdisplay",
-}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
deleted file mode 100644
index 62856df..0000000
--- a/libs/vr/libdisplay/display_client.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "include/private/dvr/display_client.h"
-
-#include <cutils/native_handle.h>
-#include <log/log.h>
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/status.h>
-
-#include <mutex>
-
-#include <private/dvr/display_protocol.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-Surface::Surface(LocalChannelHandle channel_handle, int* error)
- : BASE{pdx::default_transport::ClientChannel::Create(
- std::move(channel_handle))} {
- auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>();
- if (!status) {
- ALOGE("Surface::Surface: Failed to get surface info: %s",
- status.GetErrorMessage().c_str());
- Close(status.error());
- if (error)
- *error = status.error();
- }
-
- surface_id_ = status.get().surface_id;
- z_order_ = status.get().z_order;
- visible_ = status.get().visible;
-}
-
-Surface::Surface(const SurfaceAttributes& attributes, int* error)
- : BASE{pdx::default_transport::ClientChannelFactory::Create(
- DisplayProtocol::kClientPath),
- kInfiniteTimeout} {
- auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes);
- if (!status) {
- ALOGE("Surface::Surface: Failed to create display surface: %s",
- status.GetErrorMessage().c_str());
- Close(status.error());
- if (error)
- *error = status.error();
- }
-
- surface_id_ = status.get().surface_id;
- z_order_ = status.get().z_order;
- visible_ = status.get().visible;
-}
-
-Status<void> Surface::SetVisible(bool visible) {
- return SetAttributes(
- {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}});
-}
-
-Status<void> Surface::SetZOrder(int z_order) {
- return SetAttributes(
- {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}});
-}
-
-Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
- auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes);
- if (!status) {
- ALOGE(
- "Surface::SetAttributes: Failed to set display surface "
- "attributes: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- // Set the local cached copies of the attributes we care about from the full
- // set of attributes sent to the display service.
- for (const auto& attribute : attributes) {
- const auto& key = attribute.first;
- const auto* variant = &attribute.second;
- bool invalid_value = false;
- switch (key) {
- case SurfaceAttribute::Visible:
- invalid_value =
- !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
- break;
- case SurfaceAttribute::ZOrder:
- invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
- break;
- }
-
- if (invalid_value) {
- ALOGW(
- "Surface::SetAttributes: Failed to set display surface "
- "attribute %d because of incompatible type: %d",
- key, variant->index());
- }
- }
-
- return {};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
- uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) {
- ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
- auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(
- ProducerQueueConfigBuilder()
- .SetDefaultWidth(width)
- .SetDefaultHeight(height)
- .SetDefaultFormat(format)
- .SetMetadataSize(metadata_size)
- .Build());
- if (!status) {
- ALOGE("Surface::CreateQueue: Failed to create queue: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto producer_queue = ProducerQueue::Import(status.take());
- if (!producer_queue) {
- ALOGE("Surface::CreateQueue: Failed to import producer queue!");
- return ErrorStatus(ENOMEM);
- }
-
- return {std::move(producer_queue)};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
- uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t capacity, size_t metadata_size) {
- ALOGD_IF(TRACE,
- "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
- "usage=%" PRIx64 " capacity=%zu",
- width, height, layer_count, format, usage, capacity);
- auto status = CreateQueue(width, height, format, metadata_size);
- if (!status)
- return status.error_status();
-
- auto producer_queue = status.take();
-
- ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
- auto allocate_status = producer_queue->AllocateBuffers(
- width, height, layer_count, format, usage, capacity);
- if (!allocate_status) {
- ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
- producer_queue->id(), allocate_status.GetErrorMessage().c_str());
- return allocate_status.error_status();
- }
-
- return {std::move(producer_queue)};
-}
-
-DisplayClient::DisplayClient(int* error)
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- DisplayProtocol::kClientPath),
- kInfiniteTimeout) {
- if (error)
- *error = Client::error();
-}
-
-Status<Metrics> DisplayClient::GetDisplayMetrics() {
- return InvokeRemoteMethod<DisplayProtocol::GetMetrics>();
-}
-
-Status<std::string> DisplayClient::GetConfigurationData(
- ConfigFileType config_type) {
- auto status =
- InvokeRemoteMethod<DisplayProtocol::GetConfigurationData>(config_type);
- if (!status && status.error() != ENOENT) {
- ALOGE(
- "DisplayClient::GetConfigurationData: Unable to get"
- "configuration data. Error: %s",
- status.GetErrorMessage().c_str());
- }
- return status;
-}
-
-Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() {
- return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>();
-}
-
-Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
- const SurfaceAttributes& attributes) {
- int error;
- if (auto client = Surface::Create(attributes, &error))
- return {std::move(client)};
- else
- return ErrorStatus(error);
-}
-
-pdx::Status<std::unique_ptr<IonBuffer>> DisplayClient::SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage) {
- auto status =
- InvokeRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(key, size, usage);
- if (!status) {
- ALOGE(
- "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer "
- "%s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto ion_buffer = std::make_unique<IonBuffer>();
- auto native_buffer_handle = status.take();
- const int ret = native_buffer_handle.Import(ion_buffer.get());
- if (ret < 0) {
- ALOGE(
- "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
- "key=%d; error=%s",
- key, strerror(-ret));
- return ErrorStatus(-ret);
- }
-
- return {std::move(ion_buffer)};
-}
-
-pdx::Status<void> DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
- auto status = InvokeRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(key);
- if (!status) {
- ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s",
- status.GetErrorMessage().c_str());
- }
-
- return status;
-}
-
-Status<std::unique_ptr<IonBuffer>> DisplayClient::GetGlobalBuffer(
- DvrGlobalBufferKey key) {
- auto status = InvokeRemoteMethod<DisplayProtocol::GetGlobalBuffer>(key);
- if (!status) {
- ALOGE(
- "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; "
- "error=%s",
- key, status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto ion_buffer = std::make_unique<IonBuffer>();
- auto native_buffer_handle = status.take();
- const int ret = native_buffer_handle.Import(ion_buffer.get());
- if (ret < 0) {
- ALOGE(
- "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
- "key=%d; error=%s",
- key, strerror(-ret));
- return ErrorStatus(-ret);
- }
-
- return {std::move(ion_buffer)};
-}
-
-Status<bool> DisplayClient::IsVrAppRunning() {
- return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>();
-}
-
-} // namespace display
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
deleted file mode 100644
index fdeeb70..0000000
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "include/private/dvr/display_manager_client.h"
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <utils/Log.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-DisplayManagerClient::DisplayManagerClient()
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- DisplayManagerProtocol::kClientPath)) {}
-
-DisplayManagerClient::~DisplayManagerClient() {}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerClient::GetSurfaceState() {
- auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>();
- if (!status) {
- ALOGE(
- "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s",
- status.GetErrorMessage().c_str());
- }
-
- return status;
-}
-
-pdx::Status<std::unique_ptr<ConsumerQueue>>
-DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) {
- auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
- surface_id, queue_id);
- if (!status) {
- ALOGE(
- "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for "
- "surface_id=%d queue_id=%d: %s",
- surface_id, queue_id, status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return {ConsumerQueue::Import(status.take())};
-}
-
-} // namespace display
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp
deleted file mode 100644
index 773f9a5..0000000
--- a/libs/vr/libdisplay/display_protocol.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "include/private/dvr/display_protocol.h"
-
-namespace android {
-namespace dvr {
-namespace display {
-
-constexpr char DisplayProtocol::kClientPath[];
-constexpr char DisplayManagerProtocol::kClientPath[];
-constexpr char VSyncProtocol::kClientPath[];
-
-} // namespace display
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
deleted file mode 100644
index 81546ac..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_CLIENT_H_
-
-#include <dvr/dvr_api.h>
-#include <hardware/hwcomposer.h>
-#include <pdx/client.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-namespace display {
-
-class Surface : public pdx::ClientBase<Surface> {
- public:
- // Utility named constructor. This can be removed once ClientBase::Create is
- // refactored to return Status<T> types.
- static pdx::Status<std::unique_ptr<Surface>> CreateSurface(
- const SurfaceAttributes& attributes) {
- int error;
- pdx::Status<std::unique_ptr<Surface>> status;
- if (auto surface = Create(attributes, &error))
- status.SetValue(std::move(surface));
- else
- status.SetError(error);
- return status;
- }
-
- int surface_id() const { return surface_id_; }
- int z_order() const { return z_order_; }
- bool visible() const { return visible_; }
-
- pdx::Status<void> SetVisible(bool visible);
- pdx::Status<void> SetZOrder(int z_order);
- pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);
-
- // Creates an empty queue.
- pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
- uint32_t height,
- uint32_t format,
- size_t metadata_size);
-
- // Creates a queue and populates it with |capacity| buffers of the specified
- // parameters.
- pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
- uint32_t height,
- uint32_t layer_count,
- uint32_t format,
- uint64_t usage,
- size_t capacity,
- size_t metadata_size);
-
- private:
- friend BASE;
-
- int surface_id_ = -1;
- int z_order_ = 0;
- bool visible_ = false;
-
- // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create()
- // returns Status<T>.
- explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr);
- explicit Surface(pdx::LocalChannelHandle channel_handle,
- int* error = nullptr);
-
- Surface(const Surface&) = delete;
- void operator=(const Surface&) = delete;
-};
-
-class DisplayClient : public pdx::ClientBase<DisplayClient> {
- public:
- pdx::Status<Metrics> GetDisplayMetrics();
- pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type);
- pdx::Status<uint8_t> GetDisplayIdentificationPort();
- pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage);
- pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
- pdx::Status<std::unique_ptr<IonBuffer>> GetGlobalBuffer(
- DvrGlobalBufferKey key);
- pdx::Status<std::unique_ptr<Surface>> CreateSurface(
- const SurfaceAttributes& attributes);
-
- // Temporary query for current VR status. Will be removed later.
- pdx::Status<bool> IsVrAppRunning();
-
- private:
- friend BASE;
-
- explicit DisplayClient(int* error = nullptr);
-
- DisplayClient(const DisplayClient&) = delete;
- void operator=(const DisplayClient&) = delete;
-};
-
-} // namespace display
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
deleted file mode 100644
index 45aef51..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-
-class IonBuffer;
-class ConsumerQueue;
-
-namespace display {
-
-class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
- public:
- ~DisplayManagerClient() override;
-
- pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
- pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
- int queue_id);
-
- using Client::event_fd;
-
- pdx::Status<int> GetEventMask(int events) {
- if (auto* client_channel = GetChannel())
- return client_channel->GetEventMask(events);
- else
- return pdx::ErrorStatus(EINVAL);
- }
-
- private:
- friend BASE;
-
- DisplayManagerClient();
-
- DisplayManagerClient(const DisplayManagerClient&) = delete;
- void operator=(const DisplayManagerClient&) = delete;
-};
-
-} // namespace display
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
deleted file mode 100644
index 9f4cc4a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ /dev/null
@@ -1,304 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_
-#define ANDROID_DVR_DISPLAY_PROTOCOL_H_
-
-#include <sys/types.h>
-
-#include <array>
-#include <map>
-
-#include <dvr/dvr_display_types.h>
-
-#include <dvr/dvr_api.h>
-#include <pdx/rpc/buffer_wrapper.h>
-#include <pdx/rpc/remote_method.h>
-#include <pdx/rpc/serializable.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/bufferhub_rpc.h>
-
-// RPC protocol definitions for DVR display services (VrFlinger).
-
-namespace android {
-namespace dvr {
-namespace display {
-
-// Native display metrics.
-struct Metrics {
- // Basic display properties.
- uint32_t display_width;
- uint32_t display_height;
- uint32_t display_x_dpi;
- uint32_t display_y_dpi;
- uint32_t vsync_period_ns;
-
- // HMD metrics.
- // TODO(eieio): Determine how these fields should be populated. On phones
- // these values are determined at runtime by VrCore based on which headset the
- // phone is in. On dedicated hardware this needs to come from somewhere else.
- // Perhaps these should be moved to a separate structure that is returned by a
- // separate runtime call.
- uint32_t distorted_width;
- uint32_t distorted_height;
- uint32_t hmd_ipd_mm;
- float inter_lens_distance_m;
- std::array<float, 4> left_fov_lrbt;
- std::array<float, 4> right_fov_lrbt;
-
- private:
- PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height,
- display_x_dpi, display_y_dpi, vsync_period_ns,
- distorted_width, distorted_height, hmd_ipd_mm,
- inter_lens_distance_m, left_fov_lrbt,
- right_fov_lrbt);
-};
-
-// Serializable base type for enum structs. Enum structs are easier to use than
-// enum classes, especially for bitmasks. This base type provides common
-// utilities for flags types.
-template <typename Integer>
-class Flags {
- public:
- using Base = Flags<Integer>;
- using Type = Integer;
-
- // NOLINTNEXTLINE(google-explicit-constructor)
- Flags(const Integer& value) : value_{value} {}
- Flags(const Flags&) = default;
- Flags& operator=(const Flags&) = default;
-
- Integer value() const { return value_; }
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator Integer() const { return value_; }
-
- bool IsSet(Integer bits) const { return (value_ & bits) == bits; }
- bool IsClear(Integer bits) const { return (value_ & bits) == 0; }
-
- void Set(Integer bits) { value_ |= bits; }
- void Clear(Integer bits) { value_ &= ~bits; }
-
- Integer operator|(Integer bits) const { return value_ | bits; }
- Integer operator&(Integer bits) const { return value_ & bits; }
-
- Flags& operator|=(Integer bits) {
- value_ |= bits;
- return *this;
- }
- Flags& operator&=(Integer bits) {
- value_ &= bits;
- return *this;
- }
-
- private:
- Integer value_;
-
- PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_);
-};
-
-// Flags indicating what changed since last update.
-struct SurfaceUpdateFlags : public Flags<uint32_t> {
- enum : Type {
- None = DVR_SURFACE_UPDATE_FLAGS_NONE,
- NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE,
- BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED,
- VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED,
- AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED,
- };
-
- SurfaceUpdateFlags() : Base{None} {}
- using Base::Base;
-};
-
-// Surface attribute key/value types.
-using SurfaceAttributeKey = int32_t;
-using SurfaceAttributeValue =
- pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
- std::array<float, 3>, std::array<float, 4>,
- std::array<float, 8>, std::array<float, 16>>;
-
-// Defined surface attribute keys.
-struct SurfaceAttribute : public Flags<SurfaceAttributeKey> {
- enum : Type {
- // Keys in the negative integer space are interpreted by VrFlinger for
- // direct surfaces.
- Direct = DVR_SURFACE_ATTRIBUTE_DIRECT,
- ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE,
-
- // Invalid key. May be used to terminate C style lists in public API code.
- Invalid = DVR_SURFACE_ATTRIBUTE_INVALID,
-
- // Positive keys are interpreted by the compositor only.
- FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY,
- };
-
- SurfaceAttribute() : Base{Invalid} {}
- using Base::Base;
-};
-
-// Collection of surface attribute key/value pairs.
-using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>;
-
-struct SurfaceState {
- int32_t surface_id;
- int32_t process_id;
- int32_t user_id;
-
- SurfaceAttributes surface_attributes;
- SurfaceUpdateFlags update_flags;
- std::vector<int32_t> queue_ids;
-
- // Convenience accessors.
- bool GetVisible() const {
- bool bool_value = false;
- GetAttribute(SurfaceAttribute::Visible, &bool_value,
- ValidTypes<int32_t, int64_t, bool, float>{});
- return bool_value;
- }
-
- int GetZOrder() const {
- int int_value = 0;
- GetAttribute(SurfaceAttribute::ZOrder, &int_value,
- ValidTypes<int32_t, int64_t, float>{});
- return int_value;
- }
-
- private:
- template <typename... Types>
- struct ValidTypes {};
-
- template <typename ReturnType, typename... Types>
- bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value,
- ValidTypes<Types...>) const {
- auto search = surface_attributes.find(key);
- if (search != surface_attributes.end())
- return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value);
- else
- return false;
- }
-
- PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id,
- surface_attributes, update_flags, queue_ids);
-};
-
-struct SurfaceInfo {
- int surface_id;
- bool visible;
- int z_order;
-
- private:
- PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order);
-};
-
-enum class ConfigFileType : uint32_t {
- kLensMetrics,
- kDeviceMetrics,
- kDeviceConfiguration,
- kDeviceEdid
-};
-
-struct DisplayProtocol {
- // Service path.
- static constexpr char kClientPath[] = "system/vr/display/client";
-
- // Op codes.
- enum {
- kOpGetMetrics = 0,
- kOpGetConfigurationData,
- kOpSetupGlobalBuffer,
- kOpDeleteGlobalBuffer,
- kOpGetGlobalBuffer,
- kOpIsVrAppRunning,
- kOpCreateSurface,
- kOpGetSurfaceInfo,
- kOpCreateQueue,
- kOpSetAttributes,
- kOpGetDisplayIdentificationPort,
- };
-
- // Aliases.
- using LocalChannelHandle = pdx::LocalChannelHandle;
- using Void = pdx::rpc::Void;
-
- // Methods.
- PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
- PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
- std::string(ConfigFileType config_type));
- PDX_REMOTE_METHOD(GetDisplayIdentificationPort,
- kOpGetDisplayIdentificationPort, uint8_t(Void));
- PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer,
- LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size,
- uint64_t usage));
- PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer,
- void(DvrGlobalBufferKey key));
- PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer,
- LocalNativeBufferHandle(DvrGlobalBufferKey key));
- PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void));
- PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
- SurfaceInfo(const SurfaceAttributes& attributes));
- PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
- PDX_REMOTE_METHOD(
- CreateQueue, kOpCreateQueue,
- LocalChannelHandle(const ProducerQueueConfig& producer_config));
- PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
- void(const SurfaceAttributes& attributes));
-};
-
-struct DisplayManagerProtocol {
- // Service path.
- static constexpr char kClientPath[] = "system/vr/display/manager";
-
- // Op codes.
- enum {
- kOpGetSurfaceState = 0,
- kOpGetSurfaceQueue,
- };
-
- // Aliases.
- using LocalChannelHandle = pdx::LocalChannelHandle;
- using Void = pdx::rpc::Void;
-
- // Methods.
- PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState,
- std::vector<SurfaceState>(Void));
- PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue,
- LocalChannelHandle(int surface_id, int queue_id));
-};
-
-struct VSyncSchedInfo {
- int64_t vsync_period_ns;
- int64_t timestamp_ns;
- uint32_t next_vsync_count;
-
- private:
- PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
- next_vsync_count);
-};
-
-struct VSyncProtocol {
- // Service path.
- static constexpr char kClientPath[] = "system/vr/display/vsync";
-
- // Op codes.
- enum {
- kOpWait = 0,
- kOpAck,
- kOpGetLastTimestamp,
- kOpGetSchedInfo,
- kOpAcknowledge,
- };
-
- // Aliases.
- using Void = pdx::rpc::Void;
- using Timestamp = int64_t;
-
- // Methods.
- PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
- PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
- PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
- PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void));
-};
-
-} // namespace display
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h b/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
deleted file mode 100644
index 20541a6..0000000
--- a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-#define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-
-#include <assert.h>
-#include <tuple>
-
-#include <libbroadcastring/broadcast_ring.h>
-#include <private/dvr/display_client.h>
-
-namespace android {
-namespace dvr {
-
-// The buffer usage type for mapped shared buffers.
-enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY };
-
-// Holds the memory for the mapped shared buffer. Unlocks and releases the
-// underlying IonBuffer in destructor.
-class CPUMappedBuffer {
- public:
- // This constructor will create a display client and get the buffer from it.
- CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode);
-
- // If you already have the IonBuffer, use this. It will take ownership.
- CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode);
-
- // Use this if you do not want to take ownership.
- CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode);
-
- ~CPUMappedBuffer();
-
- // Getters.
- size_t Size() const { return size_; }
- void* Address() const { return address_; }
- bool IsMapped() const { return Address() != nullptr; }
-
- // Attempt mapping this buffer to the CPU addressable space.
- // This will create a display client and see if the buffer exists.
- // If the buffer has not been setup yet, you will need to try again later.
- void TryMapping();
-
- protected:
- // The memory area if we managed to map it.
- size_t size_ = 0;
- void* address_ = nullptr;
-
- // If we are polling the display client, the buffer key here.
- DvrGlobalBufferKey buffer_key_;
-
- // If we just own the IonBuffer outright, it's here.
- std::unique_ptr<IonBuffer> owned_buffer_ = nullptr;
-
- // The last time we connected to the display service.
- int64_t last_display_service_connection_ns_ = 0;
-
- // If we do not own the IonBuffer, it's here
- IonBuffer* buffer_ = nullptr;
-
- // The usage mode.
- CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN;
-};
-
-// Represents a broadcast ring inside a mapped shared memory buffer.
-// If has the same set of constructors as CPUMappedBuffer.
-// The template argument is the concrete BroadcastRing class that this buffer
-// holds.
-template <class RingType>
-class CPUMappedBroadcastRing : public CPUMappedBuffer {
- public:
- CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode)
- : CPUMappedBuffer(key, mode) {}
-
- CPUMappedBroadcastRing(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode)
- : CPUMappedBuffer(std::move(buffer), mode) {}
-
- CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode)
- : CPUMappedBuffer(buffer, mode) {}
-
- // Helper function for publishing records in the ring.
- void Publish(const typename RingType::Record& record) {
- assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) ||
- (usage_mode_ == CPUUsageMode::WRITE_RARELY));
-
- auto ring = Ring();
- if (ring) {
- ring->Put(record);
- }
- }
-
- // Helper function for getting records from the ring.
- // Returns true if we were able to retrieve the latest.
- bool GetNewest(typename RingType::Record* record) {
- assert((usage_mode_ == CPUUsageMode::READ_OFTEN) ||
- (usage_mode_ == CPUUsageMode::READ_RARELY));
-
- auto ring = Ring();
- if (ring) {
- return ring->GetNewest(&sequence_, record);
- }
-
- return false;
- }
-
- // Try obtaining the ring. If the named buffer has not been created yet, it
- // will return nullptr.
- RingType* Ring() {
- // No ring created yet?
- if (ring_ == nullptr) {
- // Not mapped the memory yet?
- if (IsMapped() == false) {
- TryMapping();
- }
-
- // If have the memory mapped, allocate the ring.
- if (IsMapped()) {
- switch (usage_mode_) {
- case CPUUsageMode::READ_OFTEN:
- case CPUUsageMode::READ_RARELY: {
- RingType ring;
- bool import_ok;
- std::tie(ring, import_ok) = RingType::Import(address_, size_);
- if (import_ok) {
- ring_ = std::make_unique<RingType>(ring);
- }
- } break;
- case CPUUsageMode::WRITE_OFTEN:
- case CPUUsageMode::WRITE_RARELY:
- ring_ =
- std::make_unique<RingType>(RingType::Create(address_, size_));
- break;
- }
- }
- }
-
- return ring_.get();
- }
-
- protected:
- std::unique_ptr<RingType> ring_ = nullptr;
-
- uint32_t sequence_ = 0;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
deleted file mode 100644
index 152464a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_service.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
-#define ANDROID_DVR_VSYNC_SERVICE_H_
-
-#include <binder/IInterface.h>
-
-namespace android {
-namespace dvr {
-
-class IVsyncCallback : public IInterface {
- public:
- DECLARE_META_INTERFACE(VsyncCallback)
-
- enum {
- ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
- };
-
- virtual status_t onVsync(int64_t vsync_timestamp) = 0;
-};
-
-class BnVsyncCallback : public BnInterface<IVsyncCallback> {
- public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-// Register a callback with IVsyncService to be notified of vsync events and
-// timestamps. There's also a shared memory vsync buffer defined in
-// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
-// memory buffer that make it preferable in certain situations:
-//
-// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
-// is always available as long as surface flinger is running.
-//
-// 2. IVsyncService will make a binder callback when a vsync event occurs. This
-// allows the client to not write code to implement periodic "get the latest
-// vsync" calls, which is necessary with the vsync shared memory buffer.
-//
-// 3. The IVsyncService provides the real vsync timestamp reported by hardware
-// composer, whereas the vsync shared memory buffer only has predicted vsync
-// times.
-class IVsyncService : public IInterface {
-public:
- DECLARE_META_INTERFACE(VsyncService)
-
- static const char* GetServiceName() { return "vrflinger_vsync"; }
-
- enum {
- REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
- UNREGISTER_CALLBACK
- };
-
- virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
- virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
-};
-
-class BnVsyncService : public BnInterface<IVsyncService> {
- public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/shared_buffer_helpers.cpp b/libs/vr/libdisplay/shared_buffer_helpers.cpp
deleted file mode 100644
index 6ebf487..0000000
--- a/libs/vr/libdisplay/shared_buffer_helpers.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-// We will not poll the display service for buffers more frequently than this.
-constexpr size_t kDisplayServiceTriesPerSecond = 2;
-} // namespace
-
-CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode)
- : buffer_key_(key), usage_mode_(mode) {
- TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer,
- CPUUsageMode mode)
- : owned_buffer_(std::move(buffer)),
- buffer_(owned_buffer_.get()),
- usage_mode_(mode) {
- TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode)
- : buffer_(buffer), usage_mode_(mode) {
- TryMapping();
-}
-
-CPUMappedBuffer::~CPUMappedBuffer() {
- if (IsMapped()) {
- buffer_->Unlock();
- }
-}
-
-void CPUMappedBuffer::TryMapping() {
- // Do we have an IonBuffer for this shared memory object?
- if (buffer_ == nullptr) {
- // Has it been too long since we last connected to the display service?
- const auto current_time_ns = GetSystemClockNs();
- if ((current_time_ns - last_display_service_connection_ns_) <
- (1e9 / kDisplayServiceTriesPerSecond)) {
- // Early exit.
- return;
- }
- last_display_service_connection_ns_ = current_time_ns;
-
- // Create a display client and get the buffer.
- auto display_client = display::DisplayClient::Create();
- if (display_client) {
- auto get_result = display_client->GetGlobalBuffer(buffer_key_);
- if (get_result.ok()) {
- owned_buffer_ = get_result.take();
- buffer_ = owned_buffer_.get();
- } else {
- // The buffer has not been created yet. This is OK, we will keep
- // retrying.
- }
- } else {
- ALOGE("Unable to create display client for shared buffer access");
- }
- }
-
- if (buffer_) {
- auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK &
- ~GRALLOC_USAGE_SW_WRITE_MASK;
-
- // Figure out the usage bits.
- switch (usage_mode_) {
- case CPUUsageMode::READ_OFTEN:
- usage |= GRALLOC_USAGE_SW_READ_OFTEN;
- break;
- case CPUUsageMode::READ_RARELY:
- usage |= GRALLOC_USAGE_SW_READ_RARELY;
- break;
- case CPUUsageMode::WRITE_OFTEN:
- usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
- break;
- case CPUUsageMode::WRITE_RARELY:
- usage |= GRALLOC_USAGE_SW_WRITE_RARELY;
- break;
- }
-
- int width = static_cast<int>(buffer_->width());
- int height = 1;
- const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_);
-
- if (ret < 0 || !address_) {
- ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_);
- buffer_->Unlock();
- } else {
- size_ = width;
- }
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/system/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
deleted file mode 100644
index 04d4f30..0000000
--- a/libs/vr/libdisplay/vsync_service.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "include/private/dvr/vsync_service.h"
-
-#include <binder/Parcel.h>
-#include <log/log.h>
-
-namespace android {
-namespace dvr {
-
-status_t BnVsyncCallback::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch (code) {
- case ON_VSYNC: {
- CHECK_INTERFACE(IVsyncCallback, data, reply);
- int64_t vsync_timestamp = 0;
- status_t result = data.readInt64(&vsync_timestamp);
- if (result != OK) {
- ALOGE("onVsync failed to readInt64: %d", result);
- return result;
- }
- onVsync(vsync_timestamp);
- return OK;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-class BpVsyncCallback : public BpInterface<IVsyncCallback> {
-public:
- explicit BpVsyncCallback(const sp<IBinder>& impl)
- : BpInterface<IVsyncCallback>(impl) {}
- virtual ~BpVsyncCallback() {}
-
- virtual status_t onVsync(int64_t vsync_timestamp) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IVsyncCallback::getInterfaceDescriptor());
- if (result != OK) {
- ALOGE("onVsync failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeInt64(vsync_timestamp);
- if (result != OK) {
- ALOGE("onVsync failed to writeInt64: %d", result);
- return result;
- }
- result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply,
- IBinder::FLAG_ONEWAY);
- if (result != OK) {
- ALOGE("onVsync failed to transact: %d", result);
- return result;
- }
- return result;
- }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
-
-
-status_t BnVsyncService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch (code) {
- case REGISTER_CALLBACK: {
- CHECK_INTERFACE(IVsyncService, data, reply);
- sp<IBinder> callback;
- status_t result = data.readStrongBinder(&callback);
- if (result != OK) {
- ALOGE("registerCallback failed to readStrongBinder: %d", result);
- return result;
- }
- registerCallback(interface_cast<IVsyncCallback>(callback));
- return OK;
- }
- case UNREGISTER_CALLBACK: {
- CHECK_INTERFACE(IVsyncService, data, reply);
- sp<IBinder> callback;
- status_t result = data.readStrongBinder(&callback);
- if (result != OK) {
- ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
- return result;
- }
- unregisterCallback(interface_cast<IVsyncCallback>(callback));
- return OK;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-class BpVsyncService : public BpInterface<IVsyncService> {
-public:
- explicit BpVsyncService(const sp<IBinder>& impl)
- : BpInterface<IVsyncService>(impl) {}
- virtual ~BpVsyncService() {}
-
- virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IVsyncService::getInterfaceDescriptor());
- if (result != OK) {
- ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(IInterface::asBinder(callback));
- if (result != OK) {
- ALOGE("registerCallback failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(
- BnVsyncService::REGISTER_CALLBACK, data, &reply);
- if (result != OK) {
- ALOGE("registerCallback failed to transact: %d", result);
- return result;
- }
- return result;
- }
-
- virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IVsyncService::getInterfaceDescriptor());
- if (result != OK) {
- ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(IInterface::asBinder(callback));
- if (result != OK) {
- ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(
- BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
- if (result != OK) {
- ALOGE("unregisterCallback failed to transact: %d", result);
- return result;
- }
- return result;
- }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 96023dd..9dbeacb 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -33,87 +33,3 @@
],
min_sdk_version: "29",
}
-
-cc_library_headers {
- name: "libdvr_private_headers",
- export_include_dirs: ["."],
- vendor_available: false,
-}
-
-cflags = [
- "-DDVR_TRACKING_IMPLEMENTED=0",
- "-DLOG_TAG=\"libdvr\"",
- "-DTRACE=0",
- "-Wall",
- "-Werror",
-]
-
-srcs = [
- "dvr_api.cpp",
- "dvr_buffer.cpp",
- "dvr_buffer_queue.cpp",
- "dvr_configuration_data.cpp",
- "dvr_display_manager.cpp",
- "dvr_hardware_composer_client.cpp",
- "dvr_performance.cpp",
- "dvr_pose.cpp",
- "dvr_surface.cpp",
- "dvr_tracking.cpp",
-]
-
-static_libs = [
- "libbroadcastring",
- "libvrsensor",
- "libdisplay",
- "libvirtualtouchpadclient",
- "libvr_hwc-impl",
- "libvr_hwc-binder",
- "libgrallocusage",
- "libperformance",
-]
-
-shared_libs = [
- "android.hardware.graphics.bufferqueue@1.0",
- "android.hidl.token@1.0-utils",
- "libbase",
- "libbufferhubqueue",
- "libbinder",
- "liblog",
- "libcutils",
- "libutils",
- "libnativewindow",
- "libgui",
- "libui",
- "libpdx_default_transport",
-]
-
-cc_library_shared {
- name: "libdvr.google",
- system_ext_specific: true,
- owner: "google",
- cflags: cflags,
- header_libs: ["libdvr_headers"],
- export_header_lib_headers: ["libdvr_headers"],
- srcs: srcs,
- static_libs: static_libs,
- shared_libs: shared_libs,
- version_script: "exported_apis.lds",
-}
-
-// Also build a static libdvr for linking into tests. The linker script
-// restricting function access in the shared lib makes it inconvenient to use in
-// test code.
-cc_library_static {
- name: "libdvr_static.google",
- owner: "google",
- cflags: cflags,
- header_libs: ["libdvr_headers"],
- export_header_lib_headers: ["libdvr_headers"],
- srcs: srcs,
- static_libs: static_libs,
- shared_libs: shared_libs,
-}
-
-subdirs = [
- "tests",
-]
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
deleted file mode 100644
index e099f6a..0000000
--- a/libs/vr/libdvr/dvr_api.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "include/dvr/dvr_api.h"
-
-#include <errno.h>
-#include <utils/Log.h>
-
-#include <algorithm>
-
-// Headers from libdvr
-#include <dvr/dvr_buffer.h>
-#include <dvr/dvr_buffer_queue.h>
-#include <dvr/dvr_configuration_data.h>
-#include <dvr/dvr_display_manager.h>
-#include <dvr/dvr_performance.h>
-#include <dvr/dvr_surface.h>
-#include <dvr/dvr_tracking.h>
-#include <dvr/dvr_vsync.h>
-
-// Headers not yet moved into libdvr.
-// TODO(jwcai) Move these once their callers are moved into Google3.
-#include <dvr/dvr_hardware_composer_client.h>
-#include <dvr/pose_client.h>
-#include <dvr/virtual_touchpad_client.h>
-
-extern "C" {
-
-int dvrGetApi(void* api, size_t struct_size, int version) {
- ALOGI("dvrGetApi: api=%p struct_size=%zu version=%d", api, struct_size,
- version);
- if (version == 1) {
- // New entry points are added at the end. If the caller's struct and
- // this library have different sizes, we define the entry points in common.
- // The caller is expected to handle unset entry points if necessary.
- size_t clamped_struct_size = std::min(struct_size, sizeof(DvrApi_v1));
- DvrApi_v1* dvr_api = static_cast<DvrApi_v1*>(api);
-
-// Defines an API entry for V1 (no version suffix).
-#define DVR_V1_API_ENTRY(name) \
- do { \
- if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \
- clamped_struct_size) { \
- dvr_api->name = dvr##name; \
- } \
- } while (0)
-
-#define DVR_V1_API_ENTRY_DEPRECATED(name) \
- do { \
- if ((offsetof(DvrApi_v1, name) + sizeof(dvr_api->name)) <= \
- clamped_struct_size) { \
- dvr_api->name = nullptr; \
- } \
- } while (0)
-
-#include "include/dvr/dvr_api_entries.h"
-
-// Undefine macro definitions to play nice with Google3 style rules.
-#undef DVR_V1_API_ENTRY
-#undef DVR_V1_API_ENTRY_DEPRECATED
-
- return 0;
- }
-
- ALOGE("dvrGetApi: Unknown API version=%d", version);
- return -EINVAL;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp
deleted file mode 100644
index c11706f..0000000
--- a/libs/vr/libdvr/dvr_buffer.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "include/dvr/dvr_buffer.h"
-
-#include <android/hardware_buffer.h>
-#include <dvr/dvr_shared_buffers.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-#include <ui/GraphicBuffer.h>
-
-#include "dvr_internal.h"
-
-using namespace android;
-
-namespace android {
-namespace dvr {
-
-DvrBuffer* CreateDvrBufferFromIonBuffer(
- const std::shared_ptr<IonBuffer>& ion_buffer) {
- if (!ion_buffer)
- return nullptr;
- return new DvrBuffer{std::move(ion_buffer)};
-}
-
-} // namespace dvr
-} // namespace android
-
-namespace {
-
-int ConvertToAHardwareBuffer(GraphicBuffer* graphic_buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!hardware_buffer || !graphic_buffer) {
- return -EINVAL;
- }
- *hardware_buffer = reinterpret_cast<AHardwareBuffer*>(graphic_buffer);
- AHardwareBuffer_acquire(*hardware_buffer);
- return 0;
-}
-
-} // anonymous namespace
-
-extern "C" {
-
-void dvrWriteBufferDestroy(DvrWriteBuffer* write_buffer) {
- if (write_buffer != nullptr) {
- ALOGW_IF(
- write_buffer->slot != -1,
- "dvrWriteBufferDestroy: Destroying a buffer associated with a valid "
- "buffer queue slot. This may indicate possible leaks, buffer_id=%d.",
- dvrWriteBufferGetId(write_buffer));
- delete write_buffer;
- }
-}
-
-int dvrWriteBufferIsValid(DvrWriteBuffer* write_buffer) {
- return write_buffer && write_buffer->write_buffer;
-}
-
-int dvrWriteBufferGetId(DvrWriteBuffer* write_buffer) {
- if (!write_buffer || !write_buffer->write_buffer)
- return -EINVAL;
-
- return write_buffer->write_buffer->id();
-}
-
-int dvrWriteBufferGetAHardwareBuffer(DvrWriteBuffer* write_buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!write_buffer || !write_buffer->write_buffer)
- return -EINVAL;
-
- return ConvertToAHardwareBuffer(
- write_buffer->write_buffer->buffer()->buffer().get(), hardware_buffer);
-}
-
-void dvrReadBufferDestroy(DvrReadBuffer* read_buffer) {
- if (read_buffer != nullptr) {
- ALOGW_IF(
- read_buffer->slot != -1,
- "dvrReadBufferDestroy: Destroying a buffer associated with a valid "
- "buffer queue slot. This may indicate possible leaks, buffer_id=%d.",
- dvrReadBufferGetId(read_buffer));
- delete read_buffer;
- }
-}
-
-int dvrReadBufferIsValid(DvrReadBuffer* read_buffer) {
- return read_buffer && read_buffer->read_buffer;
-}
-
-int dvrReadBufferGetId(DvrReadBuffer* read_buffer) {
- if (!read_buffer || !read_buffer->read_buffer)
- return -EINVAL;
-
- return read_buffer->read_buffer->id();
-}
-
-int dvrReadBufferGetAHardwareBuffer(DvrReadBuffer* read_buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!read_buffer || !read_buffer->read_buffer)
- return -EINVAL;
-
- return ConvertToAHardwareBuffer(
- read_buffer->read_buffer->buffer()->buffer().get(), hardware_buffer);
-}
-
-void dvrBufferDestroy(DvrBuffer* buffer) { delete buffer; }
-
-int dvrBufferGetAHardwareBuffer(DvrBuffer* buffer,
- AHardwareBuffer** hardware_buffer) {
- if (!buffer || !buffer->buffer || !hardware_buffer) {
- return -EINVAL;
- }
-
- return ConvertToAHardwareBuffer(buffer->buffer->buffer().get(),
- hardware_buffer);
-}
-
-// Retrieve the shared buffer layout version defined in dvr_shared_buffers.h.
-int dvrBufferGlobalLayoutVersionGet() {
- return android::dvr::kSharedBufferLayoutVersion;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp
deleted file mode 100644
index 1ca653c..0000000
--- a/libs/vr/libdvr/dvr_buffer_queue.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-#include "include/dvr/dvr_api.h"
-#include "include/dvr/dvr_buffer_queue.h"
-
-#include <android/native_window.h>
-#include <gui/BufferHubProducer.h>
-
-#include "dvr_internal.h"
-#include "dvr_buffer_queue_internal.h"
-
-using namespace android;
-using android::dvr::BufferHubBase;
-using android::dvr::ConsumerBuffer;
-using android::dvr::ConsumerQueue;
-using android::dvr::ProducerBuffer;
-using android::dvr::ProducerQueue;
-using android::dvr::ProducerQueueConfigBuilder;
-using android::dvr::UsagePolicy;
-
-extern "C" {
-
-DvrWriteBufferQueue::DvrWriteBufferQueue(
- const std::shared_ptr<ProducerQueue>& producer_queue)
- : producer_queue_(producer_queue),
- width_(producer_queue->default_width()),
- height_(producer_queue->default_height()),
- format_(producer_queue->default_format()) {}
-
-int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) {
- if (native_window_ == nullptr) {
- // Lazy creation of |native_window|, as not everyone is using
- // DvrWriteBufferQueue as an external surface.
- sp<IGraphicBufferProducer> gbp = BufferHubProducer::Create(producer_queue_);
- native_window_ = new Surface(gbp, true);
- }
-
- *out_window = static_cast<ANativeWindow*>(native_window_.get());
- return 0;
-}
-
-int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
- std::unique_ptr<ConsumerQueue> consumer_queue =
- producer_queue_->CreateConsumerQueue();
- if (consumer_queue == nullptr) {
- ALOGE(
- "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue "
- "from producer queue: queue_id=%d.", producer_queue_->id());
- return -ENOMEM;
- }
-
- *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
- return 0;
-}
-
-int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer,
- int* out_fence_fd) {
- DvrNativeBufferMetadata meta;
- DvrWriteBuffer* buffer = nullptr;
- int fence_fd = -1;
- if (const int ret = GainBuffer(timeout, &buffer, &meta, &fence_fd))
- return ret;
- if (!buffer)
- return -ENOMEM;
-
- write_buffers_[buffer->slot].reset(buffer);
- write_buffer->write_buffer = std::move(buffer->write_buffer);
- *out_fence_fd = fence_fd;
- return 0;
-}
-
-int DvrWriteBufferQueue::GainBuffer(int timeout,
- DvrWriteBuffer** out_write_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- size_t slot;
- pdx::LocalHandle release_fence;
-
- // Need to retry N+1 times, where N is total number of buffers in the queue.
- // As in the worst case, we will dequeue all N buffers and reallocate them, on
- // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension.
- size_t max_retries = 1 + producer_queue_->capacity();
- size_t retry = 0;
-
- for (; retry < max_retries; retry++) {
- auto buffer_status =
- producer_queue_->Dequeue(timeout, &slot, out_meta, &release_fence);
- if (!buffer_status) {
- ALOGE_IF(buffer_status.error() != ETIMEDOUT,
- "DvrWriteBufferQueue::GainBuffer: Failed to dequeue buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- if (write_buffers_[slot] == nullptr) {
- // Lazy initialization of a write_buffers_ slot. Note that a slot will
- // only be dynamically allocated once during the entire cycle life of a
- // queue.
- write_buffers_[slot] = std::make_unique<DvrWriteBuffer>();
- write_buffers_[slot]->slot = slot;
- }
-
- LOG_ALWAYS_FATAL_IF(
- write_buffers_[slot]->write_buffer,
- "DvrWriteBufferQueue::GainBuffer: Buffer slot is not empty: %zu", slot);
- write_buffers_[slot]->write_buffer = std::move(buffer_status.take());
-
- const auto& producer_buffer = write_buffers_[slot]->write_buffer;
- if (!producer_buffer)
- return -ENOMEM;
-
- if (width_ == producer_buffer->width() &&
- height_ == producer_buffer->height() &&
- format_ == producer_buffer->format()) {
- // Producer queue returns a buffer matches the current request.
- break;
- }
-
- // Needs reallocation. Note that if there are already multiple available
- // buffers in the queue, the next one returned from |queue_->Dequeue| may
- // still have the old buffer dimension or format. Retry up to N+1 times or
- // until we dequeued a buffer with new configuration.
- ALOGD_IF(TRACE,
- "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu "
- "(w=%u, h=%u, fmt=%u) is different from the buffer returned "
- "(w=%u, h=%u, fmt=%u). Need re-allocation.",
- slot, width_, height_, format_, producer_buffer->width(),
- producer_buffer->height(), producer_buffer->format());
-
- // Currently, we are not storing |layer_count| and |usage| in queue
- // configuration. Copy those setup from the last buffer dequeued before we
- // remove it.
- uint32_t old_layer_count = producer_buffer->layer_count();
- uint64_t old_usage = producer_buffer->usage();
-
- // Allocate a new producer buffer with new buffer configs. Note that if
- // there are already multiple available buffers in the queue, the next one
- // returned from |queue_->Dequeue| may still have the old buffer dimension
- // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until
- // we dequeued a buffer with new configuration.
- auto remove_status = producer_queue_->RemoveBuffer(slot);
- if (!remove_status) {
- ALOGE("DvrWriteBufferQueue::Dequeue: Failed to remove buffer: %s",
- remove_status.GetErrorMessage().c_str());
- return -remove_status.error();
- }
- // Make sure that the previously allocated buffer is dereferenced from
- // write_buffers_ array.
- write_buffers_[slot]->write_buffer = nullptr;
-
- auto allocate_status = producer_queue_->AllocateBuffer(
- width_, height_, old_layer_count, format_, old_usage);
- if (!allocate_status) {
- ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s",
- allocate_status.GetErrorMessage().c_str());
- return -allocate_status.error();
- }
- }
-
- if (retry >= max_retries) {
- ALOGE(
- "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after "
- "resizing.");
- return -ENOMEM;
- }
-
- *out_write_buffer = write_buffers_[slot].release();
- *out_fence_fd = release_fence.Release();
-
- return 0;
-}
-
-int DvrWriteBufferQueue::PostBuffer(DvrWriteBuffer* write_buffer,
- const DvrNativeBufferMetadata* meta,
- int ready_fence_fd) {
- // Some basic sanity checks before we put the buffer back into a slot.
- size_t slot = static_cast<size_t>(write_buffer->slot);
- LOG_FATAL_IF(
- (write_buffers->slot < 0 || write_buffers->slot >= write_buffers_.size()),
- "DvrWriteBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
-
- if (write_buffers_[slot] != nullptr) {
- ALOGE("DvrWriteBufferQueue::PostBuffer: Slot is not empty: %zu", slot);
- return -EINVAL;
- }
- if (write_buffer->write_buffer == nullptr) {
- ALOGE("DvrWriteBufferQueue::PostBuffer: Invalid write buffer.");
- return -EINVAL;
- }
- if (write_buffer->write_buffer->id() != producer_queue_->GetBufferId(slot)) {
- ALOGE(
- "DvrWriteBufferQueue::PostBuffer: Buffer to be posted does not "
- "belong to this buffer queue. Posting buffer: id=%d, buffer in "
- "queue: id=%d",
- write_buffer->write_buffer->id(), producer_queue_->GetBufferId(slot));
- return -EINVAL;
- }
-
- write_buffer->write_buffer->SetQueueIndex(next_post_index_++);
- pdx::LocalHandle fence(ready_fence_fd);
- const int ret = write_buffer->write_buffer->PostAsync(meta, fence);
- if (ret < 0) {
- ALOGE("DvrWriteBufferQueue::PostBuffer: Failed to post buffer, ret=%d",
- ret);
- return ret;
- }
-
- // Put the DvrWriteBuffer pointer back into its slot for reuse.
- write_buffers_[slot].reset(write_buffer);
- // It's import to reset the write buffer client now. It should stay invalid
- // until next GainBuffer on the same slot.
- write_buffers_[slot]->write_buffer = nullptr;
- return 0;
-}
-
-int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) {
- if (width == 0 || height == 0) {
- ALOGE(
- "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, "
- "h=%u.",
- width, height);
- return -EINVAL;
- }
-
- width_ = width;
- height_ = height;
- return 0;
-}
-
-int dvrWriteBufferQueueCreate(uint32_t width, uint32_t height, uint32_t format,
- uint32_t layer_count, uint64_t usage,
- size_t capacity, size_t metadata_size,
- DvrWriteBufferQueue** out_write_queue) {
- if (!out_write_queue)
- return -EINVAL;
-
- auto config_builder = ProducerQueueConfigBuilder()
- .SetDefaultWidth(width)
- .SetDefaultHeight(height)
- .SetDefaultFormat(format)
- .SetMetadataSize(metadata_size);
- std::unique_ptr<ProducerQueue> producer_queue =
- ProducerQueue::Create(config_builder.Build(), UsagePolicy{});
- if (!producer_queue) {
- ALOGE("dvrWriteBufferQueueCreate: Failed to create producer queue.");
- return -ENOMEM;
- }
-
- auto status = producer_queue->AllocateBuffers(width, height, layer_count,
- format, usage, capacity);
- if (!status.ok()) {
- ALOGE("dvrWriteBufferQueueCreate: Failed to allocate buffers.");
- return -ENOMEM;
- }
-
- *out_write_queue = new DvrWriteBufferQueue(std::move(producer_queue));
- return 0;
-}
-
-void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
- delete write_queue;
-}
-
-ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
- if (!write_queue)
- return -EINVAL;
-
- return write_queue->capacity();
-}
-
-int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) {
- if (!write_queue)
- return -EINVAL;
-
- return write_queue->id();
-}
-
-int dvrWriteBufferQueueGetANativeWindow(DvrWriteBufferQueue* write_queue,
- ANativeWindow** out_window) {
- if (!write_queue || !out_window)
- return -EINVAL;
-
- return write_queue->GetNativeWindow(out_window);
-}
-
-int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
- DvrReadBufferQueue** out_read_queue) {
- if (!write_queue || !out_read_queue)
- return -EINVAL;
-
- return write_queue->CreateReadQueue(out_read_queue);
-}
-
-int dvrWriteBufferQueueGainBuffer(DvrWriteBufferQueue* write_queue, int timeout,
- DvrWriteBuffer** out_write_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- if (!write_queue || !out_write_buffer || !out_meta || !out_fence_fd)
- return -EINVAL;
-
- return write_queue->GainBuffer(timeout, out_write_buffer, out_meta,
- out_fence_fd);
-}
-
-int dvrWriteBufferQueuePostBuffer(DvrWriteBufferQueue* write_queue,
- DvrWriteBuffer* write_buffer,
- const DvrNativeBufferMetadata* meta,
- int ready_fence_fd) {
- if (!write_queue || !write_buffer || !write_buffer->write_buffer || !meta)
- return -EINVAL;
-
- return write_queue->PostBuffer(write_buffer, meta, ready_fence_fd);
-}
-
-int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue,
- uint32_t width, uint32_t height) {
- if (!write_queue)
- return -EINVAL;
-
- return write_queue->ResizeBuffer(width, height);
-}
-
-// ReadBufferQueue
-
-DvrReadBufferQueue::DvrReadBufferQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue)
- : consumer_queue_(consumer_queue) {}
-
-int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
- std::unique_ptr<ConsumerQueue> consumer_queue =
- consumer_queue_->CreateConsumerQueue();
- if (consumer_queue == nullptr) {
- ALOGE(
- "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue "
- "from producer queue: queue_id=%d.", consumer_queue_->id());
- return -ENOMEM;
- }
-
- *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
- return 0;
-}
-
-int DvrReadBufferQueue::AcquireBuffer(int timeout,
- DvrReadBuffer** out_read_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- size_t slot;
- pdx::LocalHandle acquire_fence;
- auto buffer_status =
- consumer_queue_->Dequeue(timeout, &slot, out_meta, &acquire_fence);
- if (!buffer_status) {
- ALOGE_IF(buffer_status.error() != ETIMEDOUT,
- "DvrReadBufferQueue::AcquireBuffer: Failed to dequeue buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- if (read_buffers_[slot] == nullptr) {
- // Lazy initialization of a read_buffers_ slot. Note that a slot will only
- // be dynamically allocated once during the entire cycle life of a queue.
- read_buffers_[slot] = std::make_unique<DvrReadBuffer>();
- read_buffers_[slot]->slot = slot;
- }
-
- LOG_FATAL_IF(
- read_buffers_[slot]->read_buffer,
- "DvrReadBufferQueue::AcquireBuffer: Buffer slot is not empty: %zu", slot);
- read_buffers_[slot]->read_buffer = std::move(buffer_status.take());
-
- *out_read_buffer = read_buffers_[slot].release();
- *out_fence_fd = acquire_fence.Release();
-
- return 0;
-}
-
-int DvrReadBufferQueue::ReleaseBuffer(DvrReadBuffer* read_buffer,
- const DvrNativeBufferMetadata* meta,
- int release_fence_fd) {
- // Some basic sanity checks before we put the buffer back into a slot.
- size_t slot = static_cast<size_t>(read_buffer->slot);
- LOG_FATAL_IF(
- (read_buffers->slot < 0 || read_buffers->slot >= read_buffers_size()),
- "DvrReadBufferQueue::ReleaseBuffer: Invalid slot: %zu", slot);
-
- if (read_buffers_[slot] != nullptr) {
- ALOGE("DvrReadBufferQueue::ReleaseBuffer: Slot is not empty: %zu", slot);
- return -EINVAL;
- }
- if (read_buffer->read_buffer == nullptr) {
- ALOGE("DvrReadBufferQueue::ReleaseBuffer: Invalid read buffer.");
- return -EINVAL;
- }
- if (read_buffer->read_buffer->id() != consumer_queue_->GetBufferId(slot)) {
- if (consumer_queue_->GetBufferId(slot) > 0) {
- ALOGE(
- "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not "
- "belong to this queue (queue_id=%d): attempting to release buffer "
- "(buffer_id=%d) at slot %d which holds a different buffer "
- "(buffer_id=%d).",
- consumer_queue_->id(), read_buffer->read_buffer->id(),
- static_cast<int>(slot), consumer_queue_->GetBufferId(slot));
- } else {
- ALOGI(
- "DvrReadBufferQueue::ReleaseBuffer: Buffer to be released may not "
- "belong to this queue (queue_id=%d): attempting to release buffer "
- "(buffer_id=%d) at slot %d which is empty.",
- consumer_queue_->id(), read_buffer->read_buffer->id(),
- static_cast<int>(slot));
- }
- }
-
- pdx::LocalHandle fence(release_fence_fd);
- int ret = read_buffer->read_buffer->ReleaseAsync(meta, fence);
- if (ret < 0) {
- ALOGE("DvrReadBufferQueue::ReleaseBuffer: Failed to release buffer, ret=%d",
- ret);
- return ret;
- }
-
- // Put the DvrReadBuffer pointer back into its slot for reuse.
- read_buffers_[slot].reset(read_buffer);
- // It's import to reset the read buffer client now. It should stay invalid
- // until next AcquireBuffer on the same slot.
- read_buffers_[slot]->read_buffer = nullptr;
- return 0;
-}
-
-void DvrReadBufferQueue::SetBufferAvailableCallback(
- DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
- if (callback == nullptr) {
- consumer_queue_->SetBufferAvailableCallback(nullptr);
- } else {
- consumer_queue_->SetBufferAvailableCallback(
- [callback, context]() { callback(context); });
- }
-}
-
-void DvrReadBufferQueue::SetBufferRemovedCallback(
- DvrReadBufferQueueBufferRemovedCallback callback, void* context) {
- if (callback == nullptr) {
- consumer_queue_->SetBufferRemovedCallback(nullptr);
- } else {
- consumer_queue_->SetBufferRemovedCallback(
- [callback, context](const std::shared_ptr<BufferHubBase>& buffer) {
- // When buffer is removed from the queue, the slot is already invalid.
- auto read_buffer = std::make_unique<DvrReadBuffer>();
- read_buffer->read_buffer =
- std::static_pointer_cast<ConsumerBuffer>(buffer);
- callback(read_buffer.release(), context);
- });
- }
-}
-
-int DvrReadBufferQueue::HandleEvents() {
- // TODO(jwcai) Probably should change HandleQueueEvents to return Status.
- consumer_queue_->HandleQueueEvents();
- return 0;
-}
-
-void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
- delete read_queue;
-}
-
-ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->capacity();
-}
-
-int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->id();
-}
-
-int dvrReadBufferQueueGetEventFd(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->event_fd();
-}
-
-int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
- DvrReadBufferQueue** out_read_queue) {
- if (!read_queue || !out_read_queue)
- return -EINVAL;
-
- return read_queue->CreateReadQueue(out_read_queue);
-}
-
-int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
- DvrReadBuffer* read_buffer, int* out_fence_fd,
- void* out_meta, size_t meta_size_bytes) {
- if (!read_queue || !read_buffer || !out_fence_fd)
- return -EINVAL;
-
- if (meta_size_bytes != 0 && !out_meta)
- return -EINVAL;
-
- return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta,
- meta_size_bytes);
-}
-
-int dvrReadBufferQueueAcquireBuffer(DvrReadBufferQueue* read_queue, int timeout,
- DvrReadBuffer** out_read_buffer,
- DvrNativeBufferMetadata* out_meta,
- int* out_fence_fd) {
- if (!read_queue || !out_read_buffer || !out_meta || !out_fence_fd)
- return -EINVAL;
-
- return read_queue->AcquireBuffer(timeout, out_read_buffer, out_meta,
- out_fence_fd);
-}
-
-int dvrReadBufferQueueReleaseBuffer(DvrReadBufferQueue* read_queue,
- DvrReadBuffer* read_buffer,
- const DvrNativeBufferMetadata* meta,
- int release_fence_fd) {
- if (!read_queue || !read_buffer || !read_buffer->read_buffer || !meta)
- return -EINVAL;
-
- return read_queue->ReleaseBuffer(read_buffer, meta, release_fence_fd);
-}
-
-int dvrReadBufferQueueSetBufferAvailableCallback(
- DvrReadBufferQueue* read_queue,
- DvrReadBufferQueueBufferAvailableCallback callback, void* context) {
- if (!read_queue)
- return -EINVAL;
-
- read_queue->SetBufferAvailableCallback(callback, context);
- return 0;
-}
-
-int dvrReadBufferQueueSetBufferRemovedCallback(
- DvrReadBufferQueue* read_queue,
- DvrReadBufferQueueBufferRemovedCallback callback, void* context) {
- if (!read_queue)
- return -EINVAL;
-
- read_queue->SetBufferRemovedCallback(callback, context);
- return 0;
-}
-
-int dvrReadBufferQueueHandleEvents(DvrReadBufferQueue* read_queue) {
- if (!read_queue)
- return -EINVAL;
-
- return read_queue->HandleEvents();
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_buffer_queue_internal.h b/libs/vr/libdvr/dvr_buffer_queue_internal.h
deleted file mode 100644
index e53a686..0000000
--- a/libs/vr/libdvr/dvr_buffer_queue_internal.h
+++ /dev/null
@@ -1,95 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
-#define ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
-
-#include <gui/Surface.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <sys/cdefs.h>
-
-#include <array>
-#include <memory>
-
-#include "dvr_internal.h"
-
-struct ANativeWindow;
-
-typedef struct DvrNativeBufferMetadata DvrNativeBufferMetadata;
-typedef struct DvrReadBuffer DvrReadBuffer;
-typedef struct DvrReadBufferQueue DvrReadBufferQueue;
-typedef struct DvrWriteBuffer DvrWriteBuffer;
-typedef void (*DvrReadBufferQueueBufferAvailableCallback)(void* context);
-typedef void (*DvrReadBufferQueueBufferRemovedCallback)(DvrReadBuffer* buffer,
- void* context);
-
-struct DvrWriteBufferQueue {
- using BufferHubQueue = android::dvr::BufferHubQueue;
- using ProducerQueue = android::dvr::ProducerQueue;
-
- // Create a concrete object for DvrWriteBufferQueue.
- //
- // @param producer_queue The BufferHub's ProducerQueue that is used to back
- // this DvrWriteBufferQueue, must not be NULL.
- explicit DvrWriteBufferQueue(
- const std::shared_ptr<ProducerQueue>& producer_queue);
-
- int id() const { return producer_queue_->id(); }
- uint32_t width() const { return width_; };
- uint32_t height() const { return height_; };
- uint32_t format() const { return format_; };
- size_t capacity() const { return producer_queue_->capacity(); }
- const std::shared_ptr<ProducerQueue>& producer_queue() const {
- return producer_queue_;
- }
-
- int GetNativeWindow(ANativeWindow** out_window);
- int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
- int Dequeue(int timeout, DvrWriteBuffer* write_buffer, int* out_fence_fd);
- int GainBuffer(int timeout, DvrWriteBuffer** out_write_buffer,
- DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
- int PostBuffer(DvrWriteBuffer* write_buffer,
- const DvrNativeBufferMetadata* meta, int ready_fence_fd);
- int ResizeBuffer(uint32_t width, uint32_t height);
-
- private:
- std::shared_ptr<ProducerQueue> producer_queue_;
- std::array<std::unique_ptr<DvrWriteBuffer>, BufferHubQueue::kMaxQueueCapacity>
- write_buffers_;
-
- int64_t next_post_index_ = 0;
- uint32_t width_;
- uint32_t height_;
- uint32_t format_;
-
- android::sp<android::Surface> native_window_;
-};
-
-struct DvrReadBufferQueue {
- using BufferHubQueue = android::dvr::BufferHubQueue;
- using ConsumerQueue = android::dvr::ConsumerQueue;
-
- explicit DvrReadBufferQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue);
-
- int id() const { return consumer_queue_->id(); }
- int event_fd() const { return consumer_queue_->queue_fd(); }
- size_t capacity() const { return consumer_queue_->capacity(); }
-
- int CreateReadQueue(DvrReadBufferQueue** out_read_queue);
- int Dequeue(int timeout, DvrReadBuffer* read_buffer, int* out_fence_fd,
- void* out_meta, size_t user_metadata_size);
- int AcquireBuffer(int timeout, DvrReadBuffer** out_read_buffer,
- DvrNativeBufferMetadata* out_meta, int* out_fence_fd);
- int ReleaseBuffer(DvrReadBuffer* read_buffer,
- const DvrNativeBufferMetadata* meta, int release_fence_fd);
- void SetBufferAvailableCallback(
- DvrReadBufferQueueBufferAvailableCallback callback, void* context);
- void SetBufferRemovedCallback(
- DvrReadBufferQueueBufferRemovedCallback callback, void* context);
- int HandleEvents();
-
- private:
- std::shared_ptr<ConsumerQueue> consumer_queue_;
- std::array<std::unique_ptr<DvrReadBuffer>, BufferHubQueue::kMaxQueueCapacity>
- read_buffers_;
-};
-
-#endif // ANDROID_DVR_BUFFER_QUEUE_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_configuration_data.cpp b/libs/vr/libdvr/dvr_configuration_data.cpp
deleted file mode 100644
index df0d54e..0000000
--- a/libs/vr/libdvr/dvr_configuration_data.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "include/dvr/dvr_configuration_data.h"
-
-#include <private/dvr/display_client.h>
-
-using android::dvr::display::ConfigFileType;
-using android::dvr::display::DisplayClient;
-
-extern "C" {
-
-int dvrConfigurationDataGet(int config_type, uint8_t** data,
- size_t* data_size) {
- if (!data || !data_size) {
- return -EINVAL;
- }
-
- auto client = DisplayClient::Create();
- if (!client) {
- ALOGE("dvrGetGlobalBuffer: Failed to create display client!");
- return -ECOMM;
- }
-
- ConfigFileType config_file_type = static_cast<ConfigFileType>(config_type);
- auto config_data_status =
- client->GetConfigurationData(config_file_type);
-
- if (!config_data_status) {
- return -config_data_status.error();
- }
-
- *data_size = config_data_status.get().size();
- *data = new uint8_t[*data_size];
- std::copy_n(config_data_status.get().begin(), *data_size, *data);
- return 0;
-}
-
-void dvrConfigurationDataDestroy(uint8_t* data) {
- delete[] data;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp
deleted file mode 100644
index 7f631e3..0000000
--- a/libs/vr/libdvr/dvr_display_manager.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-#include "include/dvr/dvr_display_manager.h"
-
-#include <dvr/dvr_buffer.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/display_manager_client.h>
-
-#include "dvr_internal.h"
-#include "dvr_buffer_queue_internal.h"
-
-using android::dvr::ConsumerBuffer;
-using android::dvr::display::DisplayManagerClient;
-using android::dvr::display::SurfaceAttribute;
-using android::dvr::display::SurfaceAttributes;
-using android::dvr::display::SurfaceState;
-using android::pdx::rpc::EmptyVariant;
-
-namespace {
-
-// Extracts type and value from the attribute Variant and writes them into the
-// respective fields of DvrSurfaceAttribute.
-struct AttributeVisitor {
- DvrSurfaceAttribute* attribute;
-
- void operator()(int32_t value) {
- attribute->value.int32_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
- }
- void operator()(int64_t value) {
- attribute->value.int64_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
- }
- void operator()(bool value) {
- attribute->value.bool_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
- }
- void operator()(float value) {
- attribute->value.float_value = value;
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
- }
- void operator()(const std::array<float, 2>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float2_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
- }
- void operator()(const std::array<float, 3>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float3_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
- }
- void operator()(const std::array<float, 4>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float4_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
- }
- void operator()(const std::array<float, 8>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float8_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
- }
- void operator()(const std::array<float, 16>& value) {
- std::copy(value.cbegin(), value.cend(), attribute->value.float16_value);
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
- }
- void operator()(EmptyVariant) {
- attribute->value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
- }
-};
-
-size_t ConvertSurfaceAttributes(const SurfaceAttributes& surface_attributes,
- DvrSurfaceAttribute* attributes,
- size_t max_count) {
- size_t count = 0;
- for (const auto& attribute : surface_attributes) {
- if (count >= max_count)
- break;
-
- // Copy the key and extract the Variant value using a visitor.
- attributes[count].key = attribute.first;
- attribute.second.Visit(AttributeVisitor{&attributes[count]});
- count++;
- }
-
- return count;
-}
-
-} // anonymous namespace
-
-extern "C" {
-
-struct DvrDisplayManager {
- std::unique_ptr<DisplayManagerClient> client;
-};
-
-struct DvrSurfaceState {
- std::vector<SurfaceState> state;
-};
-
-int dvrDisplayManagerCreate(DvrDisplayManager** client_out) {
- if (!client_out)
- return -EINVAL;
-
- auto client = DisplayManagerClient::Create();
- if (!client) {
- ALOGE("dvrDisplayManagerCreate: Failed to create display manager client!");
- return -EIO;
- }
-
- *client_out = new DvrDisplayManager{std::move(client)};
- return 0;
-}
-
-void dvrDisplayManagerDestroy(DvrDisplayManager* client) { delete client; }
-
-int dvrDisplayManagerGetEventFd(DvrDisplayManager* client) {
- if (!client)
- return -EINVAL;
-
- return client->client->event_fd();
-}
-
-int dvrDisplayManagerTranslateEpollEventMask(DvrDisplayManager* client,
- int in_events, int* out_events) {
- if (!client || !out_events)
- return -EINVAL;
-
- auto status = client->client->GetEventMask(in_events);
- if (!status)
- return -status.error();
-
- *out_events = status.get();
- return 0;
-}
-
-int dvrDisplayManagerGetSurfaceState(DvrDisplayManager* client,
- DvrSurfaceState* state) {
- if (!client || !state)
- return -EINVAL;
-
- auto status = client->client->GetSurfaceState();
- if (!status)
- return -status.error();
-
- state->state = status.take();
- return 0;
-}
-
-int dvrDisplayManagerGetReadBufferQueue(DvrDisplayManager* client,
- int surface_id, int queue_id,
- DvrReadBufferQueue** queue_out) {
- if (!client || !queue_out)
- return -EINVAL;
-
- auto status = client->client->GetSurfaceQueue(surface_id, queue_id);
- if (!status) {
- ALOGE("dvrDisplayManagerGetReadBufferQueue: Failed to get queue: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *queue_out = new DvrReadBufferQueue(status.take());
- return 0;
-}
-
-int dvrSurfaceStateCreate(DvrSurfaceState** surface_state_out) {
- if (!surface_state_out)
- return -EINVAL;
-
- *surface_state_out = new DvrSurfaceState{};
- return 0;
-}
-
-void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state) {
- delete surface_state;
-}
-
-int dvrSurfaceStateGetSurfaceCount(DvrSurfaceState* surface_state,
- size_t* count_out) {
- if (!surface_state)
- return -EINVAL;
-
- *count_out = surface_state->state.size();
- return 0;
-}
-
-int dvrSurfaceStateGetUpdateFlags(DvrSurfaceState* surface_state,
- size_t surface_index,
- DvrSurfaceUpdateFlags* flags_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *flags_out = surface_state->state[surface_index].update_flags;
- return 0;
-}
-
-int dvrSurfaceStateGetSurfaceId(DvrSurfaceState* surface_state,
- size_t surface_index, int* surface_id_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *surface_id_out = surface_state->state[surface_index].surface_id;
- return 0;
-}
-
-int dvrSurfaceStateGetProcessId(DvrSurfaceState* surface_state,
- size_t surface_index, int* process_id_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *process_id_out = surface_state->state[surface_index].process_id;
- return 0;
-}
-
-int dvrSurfaceStateGetQueueCount(DvrSurfaceState* surface_state,
- size_t surface_index, size_t* count_out) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- *count_out = surface_state->state[surface_index].queue_ids.size();
- return 0;
-}
-
-ssize_t dvrSurfaceStateGetQueueIds(DvrSurfaceState* surface_state,
- size_t surface_index, int* queue_ids,
- size_t max_count) {
- if (!surface_state || surface_index >= surface_state->state.size())
- return -EINVAL;
-
- size_t i;
- const auto& state = surface_state->state[surface_index];
- for (i = 0; i < std::min(max_count, state.queue_ids.size()); i++) {
- queue_ids[i] = state.queue_ids[i];
- }
-
- return i;
-}
-
-int dvrSurfaceStateGetZOrder(DvrSurfaceState* surface_state,
- size_t surface_index, int* z_order_out) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !z_order_out) {
- return -EINVAL;
- }
-
- *z_order_out = surface_state->state[surface_index].GetZOrder();
- return 0;
-}
-
-int dvrSurfaceStateGetVisible(DvrSurfaceState* surface_state,
- size_t surface_index, bool* visible_out) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !visible_out) {
- return -EINVAL;
- }
-
- *visible_out = surface_state->state[surface_index].GetVisible();
- return 0;
-}
-
-int dvrSurfaceStateGetAttributeCount(DvrSurfaceState* surface_state,
- size_t surface_index, size_t* count_out) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !count_out) {
- return -EINVAL;
- }
-
- *count_out = surface_state->state[surface_index].surface_attributes.size();
- return 0;
-}
-
-ssize_t dvrSurfaceStateGetAttributes(DvrSurfaceState* surface_state,
- size_t surface_index,
- DvrSurfaceAttribute* attributes,
- size_t max_count) {
- if (!surface_state || surface_index >= surface_state->state.size() ||
- !attributes) {
- return -EINVAL;
- }
-
- return ConvertSurfaceAttributes(
- surface_state->state[surface_index].surface_attributes, attributes,
- max_count);
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_hardware_composer_client.cpp b/libs/vr/libdvr/dvr_hardware_composer_client.cpp
deleted file mode 100644
index 4e87cf6..0000000
--- a/libs/vr/libdvr/dvr_hardware_composer_client.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-#include "include/dvr/dvr_hardware_composer_client.h"
-
-#include <android/dvr/IVrComposer.h>
-#include <android/dvr/BnVrComposerCallback.h>
-#include <android/hardware_buffer.h>
-#include <binder/IServiceManager.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <functional>
-#include <memory>
-#include <mutex>
-
-struct DvrHwcFrame {
- android::dvr::ComposerView::Frame frame;
-};
-
-namespace {
-
-class HwcCallback : public android::dvr::BnVrComposerCallback {
- public:
- using CallbackFunction = std::function<int(DvrHwcFrame*)>;
-
- explicit HwcCallback(const CallbackFunction& callback);
- ~HwcCallback() override;
-
- // Reset the callback. This needs to be done early to avoid use after free
- // accesses from binder thread callbacks.
- void Shutdown();
-
- std::unique_ptr<DvrHwcFrame> DequeueFrame();
-
- private:
- // android::dvr::BnVrComposerCallback:
- android::binder::Status onNewFrame(
- const android::dvr::ParcelableComposerFrame& frame,
- android::dvr::ParcelableUniqueFd* fence) override;
-
- // Protects the |callback_| from uses from multiple threads. During shutdown
- // there may be in-flight frame update events. In those cases the callback
- // access needs to be protected otherwise binder threads may access an invalid
- // callback.
- std::mutex mutex_;
- CallbackFunction callback_;
-
- HwcCallback(const HwcCallback&) = delete;
- void operator=(const HwcCallback&) = delete;
-};
-
-HwcCallback::HwcCallback(const CallbackFunction& callback)
- : callback_(callback) {}
-
-HwcCallback::~HwcCallback() {}
-
-void HwcCallback::Shutdown() {
- std::lock_guard<std::mutex> guard(mutex_);
- callback_ = nullptr;
-}
-
-android::binder::Status HwcCallback::onNewFrame(
- const android::dvr::ParcelableComposerFrame& frame,
- android::dvr::ParcelableUniqueFd* fence) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- if (!callback_) {
- fence->set_fence(android::base::unique_fd());
- return android::binder::Status::ok();
- }
-
- std::unique_ptr<DvrHwcFrame> dvr_frame(new DvrHwcFrame());
- dvr_frame->frame = frame.frame();
-
- fence->set_fence(android::base::unique_fd(callback_(dvr_frame.release())));
- return android::binder::Status::ok();
-}
-
-} // namespace
-
-struct DvrHwcClient {
- android::sp<android::dvr::IVrComposer> composer;
- android::sp<HwcCallback> callback;
-};
-
-DvrHwcClient* dvrHwcClientCreate(DvrHwcOnFrameCallback callback, void* data) {
- std::unique_ptr<DvrHwcClient> client(new DvrHwcClient());
-
- android::sp<android::IServiceManager> sm(android::defaultServiceManager());
- client->composer = android::interface_cast<android::dvr::IVrComposer>(
- sm->getService(android::dvr::IVrComposer::SERVICE_NAME()));
- if (!client->composer.get())
- return nullptr;
-
- client->callback = new HwcCallback(std::bind(callback, data,
- std::placeholders::_1));
- android::binder::Status status = client->composer->registerObserver(
- client->callback);
- if (!status.isOk())
- return nullptr;
-
- return client.release();
-}
-
-void dvrHwcClientDestroy(DvrHwcClient* client) {
- client->composer->clearObserver();
-
- // NOTE: Deleting DvrHwcClient* isn't enough since DvrHwcClient::callback is a
- // shared pointer that could be referenced from a binder thread. But the
- // client callback isn't valid past this calls so that needs to be reset.
- client->callback->Shutdown();
-
- delete client;
-}
-
-void dvrHwcFrameDestroy(DvrHwcFrame* frame) {
- delete frame;
-}
-
-DvrHwcDisplay dvrHwcFrameGetDisplayId(DvrHwcFrame* frame) {
- return frame->frame.display_id;
-}
-
-int32_t dvrHwcFrameGetDisplayWidth(DvrHwcFrame* frame) {
- return frame->frame.display_width;
-}
-
-int32_t dvrHwcFrameGetDisplayHeight(DvrHwcFrame* frame) {
- return frame->frame.display_height;
-}
-
-bool dvrHwcFrameGetDisplayRemoved(DvrHwcFrame* frame) {
- return frame->frame.removed;
-}
-
-size_t dvrHwcFrameGetLayerCount(DvrHwcFrame* frame) {
- return frame->frame.layers.size();
-}
-
-uint32_t dvrHwcFrameGetActiveConfig(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.active_config);
-}
-
-uint32_t dvrHwcFrameGetColorMode(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.color_mode);
-}
-
-void dvrHwcFrameGetColorTransform(DvrHwcFrame* frame, float* out_matrix,
- int32_t* out_hint) {
- *out_hint = frame->frame.color_transform_hint;
- memcpy(out_matrix, frame->frame.color_transform,
- sizeof(frame->frame.color_transform));
-}
-
-uint32_t dvrHwcFrameGetPowerMode(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.power_mode);
-}
-
-uint32_t dvrHwcFrameGetVsyncEnabled(DvrHwcFrame* frame) {
- return static_cast<uint32_t>(frame->frame.vsync_enabled);
-}
-
-DvrHwcLayer dvrHwcFrameGetLayerId(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].id;
-}
-
-AHardwareBuffer* dvrHwcFrameGetLayerBuffer(DvrHwcFrame* frame,
- size_t layer_index) {
- AHardwareBuffer* buffer = android::AHardwareBuffer_from_GraphicBuffer(
- frame->frame.layers[layer_index].buffer.get());
- AHardwareBuffer_acquire(buffer);
- return buffer;
-}
-
-int dvrHwcFrameGetLayerFence(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].fence->dup();
-}
-
-DvrHwcRecti dvrHwcFrameGetLayerDisplayFrame(DvrHwcFrame* frame,
- size_t layer_index) {
- return DvrHwcRecti{
- frame->frame.layers[layer_index].display_frame.left,
- frame->frame.layers[layer_index].display_frame.top,
- frame->frame.layers[layer_index].display_frame.right,
- frame->frame.layers[layer_index].display_frame.bottom,
- };
-}
-
-DvrHwcRectf dvrHwcFrameGetLayerCrop(DvrHwcFrame* frame, size_t layer_index) {
- return DvrHwcRectf{
- frame->frame.layers[layer_index].crop.left,
- frame->frame.layers[layer_index].crop.top,
- frame->frame.layers[layer_index].crop.right,
- frame->frame.layers[layer_index].crop.bottom,
- };
-}
-
-DvrHwcBlendMode dvrHwcFrameGetLayerBlendMode(DvrHwcFrame* frame,
- size_t layer_index) {
- return static_cast<DvrHwcBlendMode>(
- frame->frame.layers[layer_index].blend_mode);
-}
-
-float dvrHwcFrameGetLayerAlpha(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].alpha;
-}
-
-uint32_t dvrHwcFrameGetLayerType(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].type;
-}
-
-uint32_t dvrHwcFrameGetLayerApplicationId(DvrHwcFrame* frame,
- size_t layer_index) {
- return frame->frame.layers[layer_index].app_id;
-}
-
-uint32_t dvrHwcFrameGetLayerZOrder(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].z_order;
-}
-
-void dvrHwcFrameGetLayerCursor(DvrHwcFrame* frame, size_t layer_index,
- int32_t* out_x, int32_t* out_y) {
- *out_x = frame->frame.layers[layer_index].cursor_x;
- *out_y = frame->frame.layers[layer_index].cursor_y;
-}
-
-uint32_t dvrHwcFrameGetLayerTransform(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].transform;
-}
-
-uint32_t dvrHwcFrameGetLayerDataspace(DvrHwcFrame* frame, size_t layer_index) {
- return frame->frame.layers[layer_index].dataspace;
-}
-
-uint32_t dvrHwcFrameGetLayerColor(DvrHwcFrame* frame, size_t layer_index) {
- const auto& color = frame->frame.layers[layer_index].color;
- return color.r | (static_cast<uint32_t>(color.g) << 8) |
- (static_cast<uint32_t>(color.b) << 16) |
- (static_cast<uint32_t>(color.a) << 24);
-}
-
-uint32_t dvrHwcFrameGetLayerNumVisibleRegions(DvrHwcFrame* frame,
- size_t layer_index) {
- return frame->frame.layers[layer_index].visible_regions.size();
-}
-
-DvrHwcRecti dvrHwcFrameGetLayerVisibleRegion(DvrHwcFrame* frame,
- size_t layer_index, size_t index) {
- return DvrHwcRecti{
- frame->frame.layers[layer_index].visible_regions[index].left,
- frame->frame.layers[layer_index].visible_regions[index].top,
- frame->frame.layers[layer_index].visible_regions[index].right,
- frame->frame.layers[layer_index].visible_regions[index].bottom,
- };
-}
-
-uint32_t dvrHwcFrameGetLayerNumDamagedRegions(DvrHwcFrame* frame,
- size_t layer_index) {
- return frame->frame.layers[layer_index].damaged_regions.size();
-}
-
-DvrHwcRecti dvrHwcFrameGetLayerDamagedRegion(DvrHwcFrame* frame,
- size_t layer_index, size_t index) {
- return DvrHwcRecti{
- frame->frame.layers[layer_index].damaged_regions[index].left,
- frame->frame.layers[layer_index].damaged_regions[index].top,
- frame->frame.layers[layer_index].damaged_regions[index].right,
- frame->frame.layers[layer_index].damaged_regions[index].bottom,
- };
-}
diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h
deleted file mode 100644
index f845cd8..0000000
--- a/libs/vr/libdvr/dvr_internal.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef ANDROID_DVR_INTERNAL_H_
-#define ANDROID_DVR_INTERNAL_H_
-
-#include <sys/cdefs.h>
-
-#include <memory>
-
-extern "C" {
-
-typedef struct DvrBuffer DvrBuffer;
-typedef struct DvrReadBuffer DvrReadBuffer;
-typedef struct DvrWriteBuffer DvrWriteBuffer;
-
-} // extern "C"
-
-namespace android {
-namespace dvr {
-
-class IonBuffer;
-
-DvrBuffer* CreateDvrBufferFromIonBuffer(
- const std::shared_ptr<IonBuffer>& ion_buffer);
-
-} // namespace dvr
-} // namespace android
-
-extern "C" {
-
-struct DvrWriteBuffer {
- // The slot nubmer of the buffer, a valid slot number must be in the range of
- // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
- // DvrWriteBuffer acquired from a DvrWriteBufferQueue.
- int32_t slot = -1;
-
- std::shared_ptr<android::dvr::ProducerBuffer> write_buffer;
-};
-
-struct DvrReadBuffer {
- // The slot nubmer of the buffer, a valid slot number must be in the range of
- // [0, android::BufferQueueDefs::NUM_BUFFER_SLOTS). This is only valid for
- // DvrReadBuffer acquired from a DvrReadBufferQueue.
- int32_t slot = -1;
-
- std::shared_ptr<android::dvr::ConsumerBuffer> read_buffer;
-};
-
-struct DvrBuffer {
- std::shared_ptr<android::dvr::IonBuffer> buffer;
-};
-
-} // extern "C"
-
-#endif // ANDROID_DVR_INTERNAL_H_
diff --git a/libs/vr/libdvr/dvr_performance.cpp b/libs/vr/libdvr/dvr_performance.cpp
deleted file mode 100644
index 599101f..0000000
--- a/libs/vr/libdvr/dvr_performance.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "include/dvr/dvr_performance.h"
-
-#include <private/dvr/performance_client.h>
-
-using android::dvr::PerformanceClient;
-
-extern "C" {
-
-int dvrPerformanceSetSchedulerPolicy(pid_t task_id,
- const char* scheduler_policy) {
- int error;
- if (auto client = PerformanceClient::Create(&error))
- return client->SetSchedulerPolicy(task_id, scheduler_policy);
- else
- return error;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_pose.cpp b/libs/vr/libdvr/dvr_pose.cpp
deleted file mode 100644
index c379ef5..0000000
--- a/libs/vr/libdvr/dvr_pose.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "include/dvr/dvr_pose.h"
-
-#include <memory>
-
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/pose_client_internal.h>
-
-#include "dvr_buffer_queue_internal.h"
-
-using android::dvr::ConsumerQueue;
-
-int dvrPoseClientGetDataReader(DvrPoseClient* client, uint64_t data_type,
- DvrReadBufferQueue** queue_out) {
- if (!client || !queue_out)
- return -EINVAL;
-
- ConsumerQueue* consumer_queue;
- int status = android::dvr::dvrPoseClientGetDataReaderHandle(client,
- data_type,
- &consumer_queue);
- if (status != 0) {
- ALOGE("dvrPoseClientGetDataReader: Failed to get queue: %d", status);
- return status;
- }
-
- std::shared_ptr<ConsumerQueue> consumer_queue_ptr{consumer_queue};
- *queue_out = new DvrReadBufferQueue(consumer_queue_ptr);
- return 0;
-}
diff --git a/libs/vr/libdvr/dvr_surface.cpp b/libs/vr/libdvr/dvr_surface.cpp
deleted file mode 100644
index 0c7ec01..0000000
--- a/libs/vr/libdvr/dvr_surface.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-#include "include/dvr/dvr_surface.h"
-
-#include <inttypes.h>
-
-#include <pdx/rpc/variant.h>
-#include <private/android/AHardwareBufferHelpers.h>
-#include <private/dvr/display_client.h>
-
-#include "dvr_buffer_queue_internal.h"
-#include "dvr_internal.h"
-
-using android::AHardwareBuffer_convertToGrallocUsageBits;
-using android::dvr::display::DisplayClient;
-using android::dvr::display::Surface;
-using android::dvr::display::SurfaceAttributes;
-using android::dvr::display::SurfaceAttributeValue;
-using android::pdx::rpc::EmptyVariant;
-
-namespace {
-
-// Sets the Variant |destination| to the target std::array type and copies the C
-// array into it. Unsupported std::array configurations will fail to compile.
-template <typename T, std::size_t N>
-void ArrayCopy(SurfaceAttributeValue* destination, const T (&source)[N]) {
- using ArrayType = std::array<T, N>;
- *destination = ArrayType{};
- std::copy(std::begin(source), std::end(source),
- std::get<ArrayType>(*destination).begin());
-}
-
-bool ConvertSurfaceAttributes(const DvrSurfaceAttribute* attributes,
- size_t attribute_count,
- SurfaceAttributes* surface_attributes,
- size_t* error_index) {
- for (size_t i = 0; i < attribute_count; i++) {
- SurfaceAttributeValue value;
- switch (attributes[i].value.type) {
- case DVR_SURFACE_ATTRIBUTE_TYPE_INT32:
- value = attributes[i].value.int32_value;
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_INT64:
- value = attributes[i].value.int64_value;
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_BOOL:
- // bool_value is defined in an extern "C" block, which makes it look
- // like an int to C++. Use a cast to assign the correct type to the
- // Variant type SurfaceAttributeValue.
- value = static_cast<bool>(attributes[i].value.bool_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT:
- value = attributes[i].value.float_value;
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2:
- ArrayCopy(&value, attributes[i].value.float2_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3:
- ArrayCopy(&value, attributes[i].value.float3_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4:
- ArrayCopy(&value, attributes[i].value.float4_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8:
- ArrayCopy(&value, attributes[i].value.float8_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16:
- ArrayCopy(&value, attributes[i].value.float16_value);
- break;
- case DVR_SURFACE_ATTRIBUTE_TYPE_NONE:
- value = EmptyVariant{};
- break;
- default:
- *error_index = i;
- return false;
- }
-
- surface_attributes->emplace(attributes[i].key, value);
- }
-
- return true;
-}
-
-} // anonymous namespace
-
-extern "C" {
-
-struct DvrSurface {
- std::unique_ptr<Surface> surface;
-};
-
-int dvrSurfaceCreate(const DvrSurfaceAttribute* attributes,
- size_t attribute_count, DvrSurface** out_surface) {
- if (out_surface == nullptr) {
- ALOGE("dvrSurfaceCreate: Invalid inputs: out_surface=%p.", out_surface);
- return -EINVAL;
- }
-
- size_t error_index;
- SurfaceAttributes surface_attributes;
- if (!ConvertSurfaceAttributes(attributes, attribute_count,
- &surface_attributes, &error_index)) {
- ALOGE("dvrSurfaceCreate: Invalid surface attribute type: %" PRIu64,
- attributes[error_index].value.type);
- return -EINVAL;
- }
-
- auto status = Surface::CreateSurface(surface_attributes);
- if (!status) {
- ALOGE("dvrSurfaceCreate:: Failed to create display surface: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *out_surface = new DvrSurface{status.take()};
- return 0;
-}
-
-void dvrSurfaceDestroy(DvrSurface* surface) { delete surface; }
-
-int dvrSurfaceGetId(DvrSurface* surface) {
- return surface->surface->surface_id();
-}
-
-int dvrSurfaceSetAttributes(DvrSurface* surface,
- const DvrSurfaceAttribute* attributes,
- size_t attribute_count) {
- if (surface == nullptr || attributes == nullptr) {
- ALOGE(
- "dvrSurfaceSetAttributes: Invalid inputs: surface=%p attributes=%p "
- "attribute_count=%zu",
- surface, attributes, attribute_count);
- return -EINVAL;
- }
-
- size_t error_index;
- SurfaceAttributes surface_attributes;
- if (!ConvertSurfaceAttributes(attributes, attribute_count,
- &surface_attributes, &error_index)) {
- ALOGE("dvrSurfaceSetAttributes: Invalid surface attribute type: %" PRIu64,
- attributes[error_index].value.type);
- return -EINVAL;
- }
-
- auto status = surface->surface->SetAttributes(surface_attributes);
- if (!status) {
- ALOGE("dvrSurfaceSetAttributes: Failed to set attributes: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- return 0;
-}
-
-int dvrSurfaceCreateWriteBufferQueue(DvrSurface* surface, uint32_t width,
- uint32_t height, uint32_t format,
- uint32_t layer_count, uint64_t usage,
- size_t capacity, size_t metadata_size,
- DvrWriteBufferQueue** out_writer) {
- if (surface == nullptr || out_writer == nullptr) {
- ALOGE(
- "dvrSurfaceCreateWriteBufferQueue: Invalid inputs: surface=%p, "
- "out_writer=%p.",
- surface, out_writer);
- return -EINVAL;
- }
-
- auto status = surface->surface->CreateQueue(
- width, height, layer_count, format, usage, capacity, metadata_size);
- if (!status) {
- ALOGE("dvrSurfaceCreateWriteBufferQueue: Failed to create queue: %s",
- status.GetErrorMessage().c_str());
- return -status.error();
- }
-
- *out_writer = new DvrWriteBufferQueue(status.take());
- return 0;
-}
-
-int dvrSetupGlobalBuffer(DvrGlobalBufferKey key, size_t size, uint64_t usage,
- DvrBuffer** buffer_out) {
- if (!buffer_out)
- return -EINVAL;
-
- int error;
- auto client = DisplayClient::Create(&error);
- if (!client) {
- ALOGE("dvrSetupGlobalBuffer: Failed to create display client: %s",
- strerror(-error));
- return error;
- }
-
- uint64_t gralloc_usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
-
- auto buffer_status = client->SetupGlobalBuffer(key, size, gralloc_usage);
- if (!buffer_status) {
- ALOGE("dvrSetupGlobalBuffer: Failed to setup global buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- *buffer_out = CreateDvrBufferFromIonBuffer(buffer_status.take());
- return 0;
-}
-
-int dvrDeleteGlobalBuffer(DvrGlobalBufferKey key) {
- int error;
- auto client = DisplayClient::Create(&error);
- if (!client) {
- ALOGE("dvrDeleteGlobalBuffer: Failed to create display client: %s",
- strerror(-error));
- return error;
- }
-
- auto buffer_status = client->DeleteGlobalBuffer(key);
- if (!buffer_status) {
- ALOGE("dvrDeleteGlobalBuffer: Failed to delete named buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return -buffer_status.error();
- }
-
- return 0;
-}
-
-int dvrGetGlobalBuffer(DvrGlobalBufferKey key, DvrBuffer** out_buffer) {
- if (!out_buffer)
- return -EINVAL;
-
- int error;
- auto client = DisplayClient::Create(&error);
- if (!client) {
- ALOGE("dvrGetGlobalBuffer: Failed to create display client: %s",
- strerror(-error));
- return error;
- }
-
- auto status = client->GetGlobalBuffer(key);
- if (!status) {
- return -status.error();
- }
- *out_buffer = CreateDvrBufferFromIonBuffer(status.take());
- return 0;
-}
-
-int dvrGetNativeDisplayMetrics(size_t sizeof_metrics,
- DvrNativeDisplayMetrics* metrics) {
- ALOGE_IF(sizeof_metrics != sizeof(DvrNativeDisplayMetrics),
- "dvrGetNativeDisplayMetrics: metrics struct mismatch, your dvr api "
- "header is out of date.");
-
- auto client = DisplayClient::Create();
- if (!client) {
- ALOGE("dvrGetNativeDisplayMetrics: Failed to create display client!");
- return -ECOMM;
- }
-
- if (metrics == nullptr) {
- ALOGE("dvrGetNativeDisplayMetrics: output metrics buffer must be non-null");
- return -EINVAL;
- }
-
- auto status = client->GetDisplayMetrics();
-
- if (!status) {
- return -status.error();
- }
-
- if (sizeof_metrics >= 20) {
- metrics->display_width = status.get().display_width;
- metrics->display_height = status.get().display_height;
- metrics->display_x_dpi = status.get().display_x_dpi;
- metrics->display_y_dpi = status.get().display_y_dpi;
- metrics->vsync_period_ns = status.get().vsync_period_ns;
- }
-
- return 0;
-}
-
-} // extern "C"
diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp
deleted file mode 100644
index 73addc9..0000000
--- a/libs/vr/libdvr/dvr_tracking.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "include/dvr/dvr_tracking.h"
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#if !DVR_TRACKING_IMPLEMENTED
-
-extern "C" {
-
-// This file provides the stub implementation of dvrTrackingXXX APIs. On
-// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the
-// build file.
-int dvrTrackingCameraCreate(DvrTrackingCamera**) {
- ALOGE("dvrTrackingCameraCreate is not implemented.");
- return -ENOSYS;
-}
-
-void dvrTrackingCameraDestroy(DvrTrackingCamera*) {
- ALOGE("dvrTrackingCameraDestroy is not implemented.");
-}
-
-int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) {
- ALOGE("dvrTrackingCameraCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingCameraStop(DvrTrackingCamera*) {
- ALOGE("dvrTrackingCameraCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) {
- ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
- return -ENOSYS;
-}
-
-void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) {
- ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented.");
-}
-
-int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*,
- DvrTrackingFeatureCallback, void*) {
- ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) {
- ALOGE("dvrTrackingFeatureExtractorCreate is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*,
- DvrReadBuffer*,
- const DvrTrackingBufferMetadata*,
- bool*) {
- ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) {
- ALOGE("dvrTrackingSensorsCreate is not implemented.");
- return -ENOSYS;
-}
-
-void dvrTrackingSensorsDestroy(DvrTrackingSensors*) {
- ALOGE("dvrTrackingSensorsDestroy is not implemented.");
-}
-
-int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback,
- void*) {
- ALOGE("dvrTrackingStart is not implemented.");
- return -ENOSYS;
-}
-
-int dvrTrackingSensorsStop(DvrTrackingSensors*) {
- ALOGE("dvrTrackingStop is not implemented.");
- return -ENOSYS;
-}
-
-} // extern "C"
-
-#endif // DVR_TRACKING_IMPLEMENTED
diff --git a/libs/vr/libdvr/exported_apis.lds b/libs/vr/libdvr/exported_apis.lds
deleted file mode 100644
index 5ecb498..0000000
--- a/libs/vr/libdvr/exported_apis.lds
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- global:
- # Whitelist the function to load the dvr api.
- dvrGetApi;
-
- local:
- # Hide everything else.
- *;
-};
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
deleted file mode 100644
index fe7feb8..0000000
--- a/libs/vr/libdvr/tests/Android.bp
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2017 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_test {
- srcs: [
- "dvr_display_manager-test.cpp",
- "dvr_named_buffer-test.cpp",
- "dvr_tracking-test.cpp",
- ],
-
- header_libs: ["libdvr_headers"],
- static_libs: [
- "libdvr_static.google",
- "libchrome",
- "libdvrcommon",
- "libdisplay",
- "libbroadcastring",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
- ],
- cflags: [
- "-DDVR_TRACKING_IMPLEMENTED=0",
- "-DLOG_TAG=\"dvr_api-test\"",
- "-DTRACE=0",
- "-Wno-missing-field-initializers",
- "-O0",
- "-g",
- ],
- name: "dvr_api-test",
-}
-
-cc_test {
- name: "dvr_buffer_queue-test",
-
- // Includes the dvr_api.h header. Tests should only include "dvr_api.h",
- // and shall only get access to |dvrGetApi|, as other symbols are hidden
- // from the library.
- include_dirs: ["frameworks/native/libs/vr/libdvr/include"],
-
- srcs: ["dvr_buffer_queue-test.cpp"],
-
- shared_libs: [
- "libandroid",
- "liblog",
- ],
-
- cflags: [
- "-DTRACE=0",
- "-O2",
- "-g",
- ],
-
- // DTS Should only link to NDK libraries.
- sdk_version: "26",
- stl: "c++_static",
-}
-
-cc_test {
- name: "dvr_display-test",
-
- include_dirs: [
- "frameworks/native/libs/vr/libdvr/include",
- "frameworks/native/libs/nativewindow/include",
- ],
-
- srcs: ["dvr_display-test.cpp"],
-
- shared_libs: [
- "libandroid",
- "liblog",
- ],
-
- cflags: [
- "-DTRACE=0",
- "-O2",
- "-g",
- ],
-
- // DTS Should only link to NDK libraries.
- sdk_version: "26",
- stl: "c++_static",
-}
diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h
deleted file mode 100644
index 5d2ec28..0000000
--- a/libs/vr/libdvr/tests/dvr_api_test.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <dlfcn.h>
-#include <dvr/dvr_api.h>
-
-#include <gtest/gtest.h>
-
-/** DvrTestBase loads the libdvr.so at runtime and get the Dvr API version 1. */
-class DvrApiTest : public ::testing::Test {
- protected:
- void SetUp() override {
- int flags = RTLD_NOW | RTLD_LOCAL;
-
- // Here we need to ensure that libdvr is loaded with RTLD_NODELETE flag set
- // (so that calls to `dlclose` don't actually unload the library). This is a
- // workaround for an Android NDK bug. See more detail:
- // https://github.com/android-ndk/ndk/issues/360
- flags |= RTLD_NODELETE;
- platform_handle_ = dlopen("libdvr.google.so", flags);
- ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing.";
-
- auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>(
- dlsym(platform_handle_, "dvrGetApi"));
- ASSERT_NE(nullptr, dvr_get_api) << "Platform library missing dvrGetApi.";
-
- ASSERT_EQ(dvr_get_api(&api_, sizeof(api_), /*version=*/1), 0)
- << "Unable to find compatible Dvr API.";
- }
-
- void TearDown() override {
- if (platform_handle_ != nullptr) {
- dlclose(platform_handle_);
- }
- }
-
- void* platform_handle_ = nullptr;
- DvrApi_v1 api_;
-};
diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
deleted file mode 100644
index df06097..0000000
--- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp
+++ /dev/null
@@ -1,579 +0,0 @@
-#include <android/log.h>
-#include <android/native_window.h>
-#include <dvr/dvr_api.h>
-#include <dvr/dvr_buffer_queue.h>
-
-#include <gtest/gtest.h>
-
-#include <array>
-#include <unordered_map>
-
-#include "dvr_api_test.h"
-
-#define LOG_TAG "dvr_buffer_queue-test"
-
-#ifndef ALOGD
-#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
-#endif
-
-#ifndef ALOGD_IF
-#define ALOGD_IF(cond, ...) \
- ((__predict_false(cond)) ? ((void)ALOGD(__VA_ARGS__)) : (void)0)
-#endif
-
-namespace {
-
-static constexpr uint32_t kBufferWidth = 100;
-static constexpr uint32_t kBufferHeight = 1;
-static constexpr uint32_t kLayerCount = 1;
-static constexpr uint32_t kBufferFormat = AHARDWAREBUFFER_FORMAT_BLOB;
-static constexpr uint64_t kBufferUsage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
-static constexpr size_t kQueueCapacity = 3;
-
-class DvrBufferQueueTest : public DvrApiTest {
- public:
- static void BufferAvailableCallback(void* context) {
- DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context);
- thiz->HandleBufferAvailable();
- }
-
- static void BufferRemovedCallback(DvrReadBuffer* buffer, void* context) {
- DvrBufferQueueTest* thiz = static_cast<DvrBufferQueueTest*>(context);
- thiz->HandleBufferRemoved(buffer);
- }
-
- protected:
- void TearDown() override {
- if (write_queue_ != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
- }
- DvrApiTest::TearDown();
- }
-
- void HandleBufferAvailable() {
- buffer_available_count_ += 1;
- ALOGD_IF(TRACE, "Buffer avaiable, count=%d", buffer_available_count_);
- }
-
- void HandleBufferRemoved(DvrReadBuffer* buffer) {
- buffer_removed_count_ += 1;
- ALOGD_IF(TRACE, "Buffer removed, buffer=%p, count=%d", buffer,
- buffer_removed_count_);
- }
-
- DvrWriteBufferQueue* write_queue_ = nullptr;
- int buffer_available_count_{0};
- int buffer_removed_count_{0};
-};
-
-TEST_F(DvrBufferQueueTest, WriteQueueCreateDestroy) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- api_.WriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
-}
-
-TEST_F(DvrBufferQueueTest, WriteQueueGetCapacity) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- size_t capacity = api_.WriteBufferQueueGetCapacity(write_queue_);
-
- ALOGD_IF(TRACE, "TestWrite_QueueGetCapacity, capacity=%zu", capacity);
- ASSERT_EQ(kQueueCapacity, capacity);
-}
-
-TEST_F(DvrBufferQueueTest, CreateReadQueueFromWriteQueue) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBufferQueue* read_queue = nullptr;
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-TEST_F(DvrBufferQueueTest, CreateReadQueueFromReadQueue) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBufferQueue* read_queue1 = nullptr;
- DvrReadBufferQueue* read_queue2 = nullptr;
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue1);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue1);
-
- ret = api_.ReadBufferQueueCreateReadQueue(read_queue1, &read_queue2);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue2);
- ASSERT_NE(read_queue1, read_queue2);
-
- api_.ReadBufferQueueDestroy(read_queue1);
- api_.ReadBufferQueueDestroy(read_queue2);
-}
-
-TEST_F(DvrBufferQueueTest, GainBuffer) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(ret, 0);
-
- DvrWriteBuffer* wb = nullptr;
- EXPECT_FALSE(api_.WriteBufferIsValid(wb));
-
- DvrNativeBufferMetadata meta;
- int fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb, &meta,
- &fence_fd);
- ASSERT_EQ(ret, 0);
- EXPECT_EQ(fence_fd, -1);
- EXPECT_NE(wb, nullptr);
- EXPECT_TRUE(api_.WriteBufferIsValid(wb));
-}
-
-TEST_F(DvrBufferQueueTest, AcquirePostGainRelease) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(ret, 0);
-
- DvrReadBufferQueue* read_queue = nullptr;
- DvrReadBuffer* rb = nullptr;
- DvrWriteBuffer* wb = nullptr;
- DvrNativeBufferMetadata meta1;
- DvrNativeBufferMetadata meta2;
- int fence_fd = -1;
-
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(ret, 0);
- ASSERT_NE(read_queue, nullptr);
-
- api_.ReadBufferQueueSetBufferAvailableCallback(
- read_queue, &BufferAvailableCallback, this);
-
- // Gain buffer for writing.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb,
- &meta1, &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_NE(wb, nullptr);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb));
- ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, gain buffer %p, fence_fd=%d",
- wb, fence_fd);
- close(fence_fd);
-
- // Post buffer to the read_queue.
- meta1.timestamp = 42;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1);
- ASSERT_EQ(ret, 0);
- ASSERT_FALSE(api_.WriteBufferIsValid(wb));
- wb = nullptr;
-
- // Acquire buffer for reading.
- ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb,
- &meta2, &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_NE(rb, nullptr);
-
- // Dequeue is successfully, BufferAvailableCallback should be fired once.
- ASSERT_EQ(buffer_available_count_, 1);
- ASSERT_TRUE(api_.ReadBufferIsValid(rb));
-
- // Metadata should be passed along from producer to consumer properly.
- ASSERT_EQ(meta1.timestamp, meta2.timestamp);
-
- ALOGD_IF(TRACE,
- "TestDequeuePostDequeueRelease, acquire buffer %p, fence_fd=%d", rb,
- fence_fd);
- close(fence_fd);
-
- // Release buffer to the write_queue.
- ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2,
- /*release_fence_fd=*/-1);
- ASSERT_EQ(ret, 0);
- ASSERT_FALSE(api_.ReadBufferIsValid(rb));
- rb = nullptr;
-
- // TODO(b/34387835) Currently buffer allocation has to happen after all queues
- // are initialized.
- size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue);
-
- ALOGD_IF(TRACE, "TestDequeuePostDequeueRelease, capacity=%zu", capacity);
- ASSERT_EQ(kQueueCapacity, capacity);
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-TEST_F(DvrBufferQueueTest, GetANativeWindow) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- /*capacity=*/0, /*user_metadata_size=*/0, &write_queue_);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, write_queue_);
-
- ANativeWindow* window = nullptr;
- ret = api_.WriteBufferQueueGetANativeWindow(write_queue_, &window);
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, window);
-
- uint32_t width = ANativeWindow_getWidth(window);
- uint32_t height = ANativeWindow_getHeight(window);
- uint32_t format = ANativeWindow_getFormat(window);
- ASSERT_EQ(kBufferWidth, width);
- ASSERT_EQ(kBufferHeight, height);
- ASSERT_EQ(kBufferFormat, format);
-}
-
-// Create buffer queue of three buffers and dequeue three buffers out of it.
-// Before each dequeue operation, we resize the buffer queue and expect the
-// queue always return buffer with desired dimension.
-TEST_F(DvrBufferQueueTest, ResizeBuffer) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- int fence_fd = -1;
-
- DvrNativeBufferMetadata meta;
- DvrReadBufferQueue* read_queue = nullptr;
- DvrWriteBuffer* wb1 = nullptr;
- DvrWriteBuffer* wb2 = nullptr;
- DvrWriteBuffer* wb3 = nullptr;
- AHardwareBuffer* ahb1 = nullptr;
- AHardwareBuffer* ahb2 = nullptr;
- AHardwareBuffer* ahb3 = nullptr;
- AHardwareBuffer_Desc buffer_desc;
-
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
-
- api_.ReadBufferQueueSetBufferRemovedCallback(read_queue,
- &BufferRemovedCallback, this);
-
- // Handle all pending events on the read queue.
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
-
- size_t capacity = api_.ReadBufferQueueGetCapacity(read_queue);
- ALOGD_IF(TRACE, "TestResizeBuffer, capacity=%zu", capacity);
- ASSERT_EQ(kQueueCapacity, capacity);
-
- // Resize before dequeuing.
- constexpr uint32_t w1 = 10;
- ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w1, kBufferHeight);
- ASSERT_EQ(0, ret);
-
- // Gain first buffer for writing. All buffers will be resized.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb1,
- &meta, &fence_fd);
- ASSERT_EQ(0, ret);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb1));
- ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p", wb1);
- close(fence_fd);
-
- // Check the buffer dimension.
- ret = api_.WriteBufferGetAHardwareBuffer(wb1, &ahb1);
- ASSERT_EQ(0, ret);
- AHardwareBuffer_describe(ahb1, &buffer_desc);
- ASSERT_EQ(w1, buffer_desc.width);
- ASSERT_EQ(kBufferHeight, buffer_desc.height);
- AHardwareBuffer_release(ahb1);
-
- // For the first resize, all buffers are reallocated.
- int expected_buffer_removed_count = kQueueCapacity;
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_);
-
- // Resize the queue. We are testing with blob format, keep height to be 1.
- constexpr uint32_t w2 = 20;
- ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w2, kBufferHeight);
- ASSERT_EQ(0, ret);
-
- // The next buffer we dequeued should have new width.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb2,
- &meta, &fence_fd);
- ASSERT_EQ(0, ret);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb2));
- ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb2,
- fence_fd);
- close(fence_fd);
-
- // Check the buffer dimension, should be new width
- ret = api_.WriteBufferGetAHardwareBuffer(wb2, &ahb2);
- ASSERT_EQ(0, ret);
- AHardwareBuffer_describe(ahb2, &buffer_desc);
- ASSERT_EQ(w2, buffer_desc.width);
- AHardwareBuffer_release(ahb2);
-
- // For the second resize, all but one buffers are reallocated.
- expected_buffer_removed_count += (kQueueCapacity - 1);
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_);
-
- // Resize the queue for the third time.
- constexpr uint32_t w3 = 30;
- ret = api_.WriteBufferQueueResizeBuffer(write_queue_, w3, kBufferHeight);
- ASSERT_EQ(0, ret);
-
- // The next buffer we dequeued should have new width.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb3,
- &meta, &fence_fd);
- ASSERT_EQ(0, ret);
- ASSERT_TRUE(api_.WriteBufferIsValid(wb3));
- ALOGD_IF(TRACE, "TestResizeBuffer, gain buffer %p, fence_fd=%d", wb3,
- fence_fd);
- close(fence_fd);
-
- // Check the buffer dimension, should be new width
- ret = api_.WriteBufferGetAHardwareBuffer(wb3, &ahb3);
- ASSERT_EQ(0, ret);
- AHardwareBuffer_describe(ahb3, &buffer_desc);
- ASSERT_EQ(w3, buffer_desc.width);
- AHardwareBuffer_release(ahb3);
-
- // For the third resize, all but two buffers are reallocated.
- expected_buffer_removed_count += (kQueueCapacity - 2);
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
- ASSERT_EQ(expected_buffer_removed_count, buffer_removed_count_);
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-TEST_F(DvrBufferQueueTest, ReadQueueEventFd) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- DvrReadBufferQueue* read_queue = nullptr;
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
-
- ASSERT_EQ(0, ret);
- ASSERT_NE(nullptr, read_queue);
-
- int event_fd = api_.ReadBufferQueueGetEventFd(read_queue);
- ASSERT_GT(event_fd, 0);
-}
-
-// Verifies a Dvr{Read,Write}BufferQueue contains the same set of
-// Dvr{Read,Write}Buffer(s) during their lifecycles. And for the same buffer_id,
-// the corresponding AHardwareBuffer handle stays the same.
-TEST_F(DvrBufferQueueTest, StableBufferIdAndHardwareBuffer) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(0, ret);
-
- int fence_fd = -1;
- DvrReadBufferQueue* read_queue = nullptr;
- EXPECT_EQ(0, api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue));
-
- // Read buffers.
- std::array<DvrReadBuffer*, kQueueCapacity> rbs;
- // Write buffers.
- std::array<DvrWriteBuffer*, kQueueCapacity> wbs;
- // Buffer metadata.
- std::array<DvrNativeBufferMetadata, kQueueCapacity> metas;
- // Hardware buffers for Read buffers.
- std::unordered_map<int, AHardwareBuffer*> rhbs;
- // Hardware buffers for Write buffers.
- std::unordered_map<int, AHardwareBuffer*> whbs;
-
- constexpr int kNumTests = 100;
-
- // This test runs the following operations many many times. Thus we prefer to
- // use ASSERT_XXX rather than EXPECT_XXX to avoid spamming the output.
- std::function<void(size_t i)> Gain = [&](size_t i) {
- int ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/10,
- &wbs[i], &metas[i], &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_LT(fence_fd, 0); // expect invalid fence.
- ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i]));
- int buffer_id = api_.WriteBufferGetId(wbs[i]);
- ASSERT_GT(buffer_id, 0);
-
- AHardwareBuffer* hb = nullptr;
- ASSERT_EQ(0, api_.WriteBufferGetAHardwareBuffer(wbs[i], &hb));
-
- auto whb_it = whbs.find(buffer_id);
- if (whb_it == whbs.end()) {
- // If this is a new buffer id, check that total number of unique
- // hardware buffers won't exceed queue capacity.
- ASSERT_LT(whbs.size(), kQueueCapacity);
- whbs.emplace(buffer_id, hb);
- } else {
- // If this is a buffer id we have seen before, check that the
- // buffer_id maps to the same AHardwareBuffer handle.
- ASSERT_EQ(hb, whb_it->second);
- }
- };
-
- std::function<void(size_t i)> Post = [&](size_t i) {
- ASSERT_TRUE(api_.WriteBufferIsValid(wbs[i]));
-
- metas[i].timestamp++;
- int ret = api_.WriteBufferQueuePostBuffer(write_queue_, wbs[i], &metas[i],
- /*fence=*/-1);
- ASSERT_EQ(ret, 0);
- };
-
- std::function<void(size_t i)> Acquire = [&](size_t i) {
- int ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10,
- &rbs[i], &metas[i], &fence_fd);
- ASSERT_EQ(ret, 0);
- ASSERT_LT(fence_fd, 0); // expect invalid fence.
- ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i]));
-
- int buffer_id = api_.ReadBufferGetId(rbs[i]);
- ASSERT_GT(buffer_id, 0);
-
- AHardwareBuffer* hb = nullptr;
- ASSERT_EQ(0, api_.ReadBufferGetAHardwareBuffer(rbs[i], &hb));
-
- auto rhb_it = rhbs.find(buffer_id);
- if (rhb_it == rhbs.end()) {
- // If this is a new buffer id, check that total number of unique hardware
- // buffers won't exceed queue capacity.
- ASSERT_LT(rhbs.size(), kQueueCapacity);
- rhbs.emplace(buffer_id, hb);
- } else {
- // If this is a buffer id we have seen before, check that the buffer_id
- // maps to the same AHardwareBuffer handle.
- ASSERT_EQ(hb, rhb_it->second);
- }
- };
-
- std::function<void(size_t i)> Release = [&](size_t i) {
- ASSERT_TRUE(api_.ReadBufferIsValid(rbs[i]));
-
- int ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rbs[i], &metas[i],
- /*release_fence_fd=*/-1);
- ASSERT_EQ(ret, 0);
- };
-
- // Scenario one:
- for (int i = 0; i < kNumTests; i++) {
- // Gain all write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Gain(i));
- }
- // Post all write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Post(i));
- }
- // Acquire all read buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Acquire(i));
- }
- // Release all read buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Release(i));
- }
- }
-
- // Scenario two:
- for (int i = 0; i < kNumTests; i++) {
- // Gain and post all write buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Gain(i));
- ASSERT_NO_FATAL_FAILURE(Post(i));
- }
- // Acquire and release all read buffers.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Acquire(i));
- ASSERT_NO_FATAL_FAILURE(Release(i));
- }
- }
-
- // Scenario three:
- for (int i = 0; i < kNumTests; i++) {
- // Gain all write buffers then post them in reversed order.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Gain(i));
- }
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Post(kQueueCapacity - 1 - i));
- }
-
- // Acquire all write buffers then release them in reversed order.
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Acquire(i));
- }
- for (size_t i = 0; i < kQueueCapacity; i++) {
- ASSERT_NO_FATAL_FAILURE(Release(kQueueCapacity - 1 - i));
- }
- }
-}
-
-TEST_F(DvrBufferQueueTest, ConsumerReleaseAfterProducerDestroy) {
- int ret = api_.WriteBufferQueueCreate(
- kBufferWidth, kBufferHeight, kBufferFormat, kLayerCount, kBufferUsage,
- kQueueCapacity, sizeof(DvrNativeBufferMetadata), &write_queue_);
- ASSERT_EQ(ret, 0);
-
- DvrReadBufferQueue* read_queue = nullptr;
- DvrReadBuffer* rb = nullptr;
- DvrWriteBuffer* wb = nullptr;
- DvrNativeBufferMetadata meta1;
- DvrNativeBufferMetadata meta2;
- int fence_fd = -1;
-
- ret = api_.WriteBufferQueueCreateReadQueue(write_queue_, &read_queue);
- ASSERT_EQ(ret, 0);
-
- api_.ReadBufferQueueSetBufferAvailableCallback(
- read_queue, &BufferAvailableCallback, this);
-
- // Gain buffer for writing.
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, /*timeout=*/0, &wb,
- &meta1, &fence_fd);
- ASSERT_EQ(ret, 0);
- close(fence_fd);
-
- // Post buffer to the read_queue.
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, wb, &meta1, /*fence=*/-1);
- ASSERT_EQ(ret, 0);
- wb = nullptr;
-
- // Acquire buffer for reading.
- ret = api_.ReadBufferQueueAcquireBuffer(read_queue, /*timeout=*/10, &rb,
- &meta2, &fence_fd);
- ASSERT_EQ(ret, 0);
- close(fence_fd);
-
- // Destroy the write buffer queue and make sure the reader queue is picking
- // these events up.
- api_.WriteBufferQueueDestroy(write_queue_);
- ret = api_.ReadBufferQueueHandleEvents(read_queue);
- ASSERT_EQ(0, ret);
-
- // Release buffer to the write_queue.
- ret = api_.ReadBufferQueueReleaseBuffer(read_queue, rb, &meta2,
- /*release_fence_fd=*/-1);
- ASSERT_EQ(ret, 0);
- rb = nullptr;
-
- api_.ReadBufferQueueDestroy(read_queue);
-}
-
-} // namespace
diff --git a/libs/vr/libdvr/tests/dvr_display-test.cpp b/libs/vr/libdvr/tests/dvr_display-test.cpp
deleted file mode 100644
index c72f940..0000000
--- a/libs/vr/libdvr/tests/dvr_display-test.cpp
+++ /dev/null
@@ -1,351 +0,0 @@
-#include <android/hardware_buffer.h>
-#include <android/log.h>
-#include <dvr/dvr_api.h>
-#include <dvr/dvr_display_types.h>
-#include <dvr/dvr_surface.h>
-
-#include <gtest/gtest.h>
-
-#include "dvr_api_test.h"
-
-#define LOG_TAG "dvr_display-test"
-
-#ifndef ALOGD
-#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
-#endif
-
-class DvrDisplayTest : public DvrApiTest {
- protected:
- void SetUp() override {
- DvrApiTest::SetUp();
- int ret = api_.GetNativeDisplayMetrics(sizeof(display_metrics_),
- &display_metrics_);
- ASSERT_EQ(ret, 0) << "Failed to get display metrics.";
- ALOGD(
- "display_width: %d, display_height: %d, display_x_dpi: %d, "
- "display_y_dpi: %d, vsync_period_ns: %d.",
- display_metrics_.display_width, display_metrics_.display_height,
- display_metrics_.display_x_dpi, display_metrics_.display_y_dpi,
- display_metrics_.vsync_period_ns);
- }
-
- void TearDown() override {
- if (write_queue_ != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_);
- write_queue_ = nullptr;
- }
- if (direct_surface_ != nullptr) {
- api_.SurfaceDestroy(direct_surface_);
- direct_surface_ = nullptr;
- }
- DvrApiTest::TearDown();
- }
-
- /* Convert a write buffer to an android hardware buffer and fill in
- * color_textures evenly to the buffer.
- * AssertionError if the width of the buffer is not equal to the input width,
- * AssertionError if the height of the buffer is not equal to the input
- * height.
- */
- void FillWriteBuffer(DvrWriteBuffer* write_buffer,
- const std::vector<uint32_t>& color_textures,
- uint32_t width, uint32_t height);
-
- // Write buffer queue properties.
- static constexpr uint64_t kUsage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- uint32_t kFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
- static constexpr size_t kMetadataSize = 0;
- static constexpr int kTimeoutMs = 1000; // Time for getting buffer.
- uint32_t kLayerCount = 1;
- DvrWriteBufferQueue* write_queue_ = nullptr;
- DvrSurface* direct_surface_ = nullptr;
-
- // Device display properties.
- DvrNativeDisplayMetrics display_metrics_;
-};
-
-TEST_F(DvrDisplayTest, DisplayWithOneBuffer) {
- // Create a direct surface.
- std::vector<DvrSurfaceAttribute> direct_surface_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 10},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- int ret =
- api_.SurfaceCreate(direct_surface_attributes.data(),
- direct_surface_attributes.size(), &direct_surface_);
- ASSERT_EQ(ret, 0) << "Failed to create direct surface.";
-
- // Create a buffer queue with the direct surface.
- constexpr size_t kCapacity = 1;
- uint32_t width = display_metrics_.display_width;
- uint32_t height = display_metrics_.display_height;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null.";
-
- // Get buffer from WriteBufferQueue.
- DvrWriteBuffer* write_buffer = nullptr;
- DvrNativeBufferMetadata out_meta;
- int out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(write_queue_, kTimeoutMs, &write_buffer,
- &out_meta, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the buffer.";
- ASSERT_NE(nullptr, write_buffer) << "Gained buffer should not be null.";
-
- // Color the write buffer.
- FillWriteBuffer(write_buffer,
- {0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
- width, height);
-
- // Post buffer.
- int ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta,
- ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
-
- sleep(5); // For visual check on the device under test.
- // Should observe three primary colors on the screen center.
-}
-
-TEST_F(DvrDisplayTest, DisplayWithDoubleBuffering) {
- // Create a direct surface.
- std::vector<DvrSurfaceAttribute> direct_surface_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 10},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- int ret =
- api_.SurfaceCreate(direct_surface_attributes.data(),
- direct_surface_attributes.size(), &direct_surface_);
- ASSERT_EQ(ret, 0) << "Failed to create direct surface.";
-
- // Create a buffer queue with the direct surface.
- constexpr size_t kCapacity = 2;
- uint32_t width = display_metrics_.display_width;
- uint32_t height = display_metrics_.display_height;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- ASSERT_NE(nullptr, write_queue_) << "Write buffer queue should not be null.";
-
- int num_display_cycles_in_5s = 5 / (display_metrics_.vsync_period_ns / 1e9);
- ALOGD("The number of display cycles: %d", num_display_cycles_in_5s);
- int bufferhub_id_prev_write_buffer = -1;
- for (int i = 0; i < num_display_cycles_in_5s; ++i) {
- // Get a buffer from the WriteBufferQueue.
- DvrWriteBuffer* write_buffer = nullptr;
- DvrNativeBufferMetadata out_meta;
- int out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(
- write_queue_, kTimeoutMs, &write_buffer, &out_meta, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the a write buffer.";
- ASSERT_NE(nullptr, write_buffer) << "The gained buffer should not be null.";
-
- int bufferhub_id = api_.WriteBufferGetId(write_buffer);
- ALOGD("Display cycle: %d, bufferhub id of the write buffer: %d", i,
- bufferhub_id);
- EXPECT_NE(bufferhub_id_prev_write_buffer, bufferhub_id)
- << "Double buffering should be using the two buffers in turns, not "
- "reusing the same write buffer.";
- bufferhub_id_prev_write_buffer = bufferhub_id;
-
- // Color the write buffer.
- if (i % 2) {
- FillWriteBuffer(write_buffer, {0xffff0000, 0xff00ff00, 0xff0000ff}, width,
- height);
- } else {
- FillWriteBuffer(write_buffer, {0xff00ff00, 0xff0000ff, 0xffff0000}, width,
- height);
- }
-
- // Post the write buffer.
- int ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_, write_buffer, &out_meta,
- ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
- }
- // Should observe blinking screen in secondary colors
- // although it is actually displaying primary colors.
-}
-
-TEST_F(DvrDisplayTest, DisplayWithTwoHardwareLayers) {
- // Create the direct_surface_0 of z order 10 and direct_surface_1 of z
- // order 11.
- DvrSurface* direct_surface_0 = nullptr;
- std::vector<DvrSurfaceAttribute> direct_surface_0_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 10},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- int ret =
- api_.SurfaceCreate(direct_surface_0_attributes.data(),
- direct_surface_0_attributes.size(), &direct_surface_0);
- EXPECT_EQ(ret, 0) << "Failed to create direct surface.";
-
- DvrSurface* direct_surface_1 = nullptr;
- std::vector<DvrSurfaceAttribute> direct_surface_1_attributes = {
- {.key = DVR_SURFACE_ATTRIBUTE_DIRECT,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- {.key = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- .value.int32_value = 11},
- {.key = DVR_SURFACE_ATTRIBUTE_VISIBLE,
- .value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- .value.bool_value = true},
- };
- ret =
- api_.SurfaceCreate(direct_surface_1_attributes.data(),
- direct_surface_1_attributes.size(), &direct_surface_1);
- EXPECT_EQ(ret, 0) << "Failed to create direct surface.";
-
- // Create a buffer queue for each of the direct surfaces.
- constexpr size_t kCapacity = 1;
- uint32_t width = display_metrics_.display_width;
- uint32_t height = display_metrics_.display_height;
-
- DvrWriteBufferQueue* write_queue_0 = nullptr;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_0, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_0);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- EXPECT_NE(nullptr, write_queue_0) << "Write buffer queue should not be null.";
-
- DvrWriteBufferQueue* write_queue_1 = nullptr;
- ret = api_.SurfaceCreateWriteBufferQueue(
- direct_surface_1, width, height, kFormat, kLayerCount, kUsage, kCapacity,
- kMetadataSize, &write_queue_1);
- EXPECT_EQ(0, ret) << "Failed to create buffer queue.";
- EXPECT_NE(nullptr, write_queue_1) << "Write buffer queue should not be null.";
-
- // Get a buffer from each of the write buffer queues.
- DvrWriteBuffer* write_buffer_0 = nullptr;
- DvrNativeBufferMetadata out_meta_0;
- int out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(
- write_queue_0, kTimeoutMs, &write_buffer_0, &out_meta_0, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the buffer.";
- EXPECT_NE(nullptr, write_buffer_0) << "Gained buffer should not be null.";
-
- DvrWriteBuffer* write_buffer_1 = nullptr;
- DvrNativeBufferMetadata out_meta_1;
- out_fence_fd = -1;
- ret = api_.WriteBufferQueueGainBuffer(
- write_queue_1, kTimeoutMs, &write_buffer_1, &out_meta_1, &out_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to get the buffer.";
- EXPECT_NE(nullptr, write_buffer_1) << "Gained buffer should not be null.";
-
- // Color the write buffers.
- FillWriteBuffer(write_buffer_0, {0xffff0000, 0xff00ff00, 0xff0000ff}, width,
- height);
- FillWriteBuffer(write_buffer_1, {0x7f00ff00, 0x7f0000ff, 0x7fff0000}, width,
- height);
-
- // Post buffers.
- int ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_0, write_buffer_0,
- &out_meta_0, ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
-
- ready_fence_fd = -1;
- ret = api_.WriteBufferQueuePostBuffer(write_queue_1, write_buffer_1,
- &out_meta_1, ready_fence_fd);
- EXPECT_EQ(0, ret) << "Failed to post the buffer.";
-
- sleep(5); // For visual check on the device under test.
- // Should observe three secondary colors.
-
- // Test finished. Clean up buffers and surfaces.
- if (write_queue_0 != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_0);
- write_queue_0 = nullptr;
- }
- if (write_queue_1 != nullptr) {
- api_.WriteBufferQueueDestroy(write_queue_1);
- write_queue_1 = nullptr;
- }
- if (direct_surface_0 != nullptr) {
- api_.SurfaceDestroy(direct_surface_0);
- }
- if (direct_surface_1 != nullptr) {
- api_.SurfaceDestroy(direct_surface_1);
- }
-}
-
-void DvrDisplayTest::FillWriteBuffer(
- DvrWriteBuffer* write_buffer, const std::vector<uint32_t>& color_textures,
- uint32_t width, uint32_t height) {
- uint32_t num_colors = color_textures.size();
- // Convert the first write buffer to an android hardware buffer.
- AHardwareBuffer* ah_buffer = nullptr;
- int ret = api_.WriteBufferGetAHardwareBuffer(write_buffer, &ah_buffer);
- ASSERT_EQ(0, ret) << "Failed to get a hardware buffer from the write buffer.";
- ASSERT_NE(nullptr, ah_buffer) << "AHardware buffer should not be null.";
- AHardwareBuffer_Desc ah_buffer_describe;
- AHardwareBuffer_describe(ah_buffer, &ah_buffer_describe);
- ASSERT_EQ(ah_buffer_describe.format, kFormat)
- << "The format of the android hardware buffer is wrong.";
- ASSERT_EQ(ah_buffer_describe.layers, kLayerCount)
- << "The obtained android hardware buffer should have 2 layers.";
- ASSERT_EQ(ah_buffer_describe.width, width)
- << "The obtained android hardware buffer width is wrong.";
- ASSERT_EQ(ah_buffer_describe.height, height)
- << "The obtained android hardware buffer height is wrong.";
- // Change the content of the android hardware buffer.
- void* buffer_data = nullptr;
- int32_t fence = -1;
- ret = AHardwareBuffer_lock(ah_buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
- fence, nullptr, &buffer_data);
- ASSERT_EQ(0, ret) << "Failed to lock the hardware buffer.";
- ASSERT_NE(nullptr, buffer_data) << "Buffer data should not be null.";
-
- uint32_t num_pixels = width * height / num_colors;
- for (uint32_t color_index = 0; color_index < num_colors - 1; ++color_index) {
- uint32_t color_texture = color_textures[color_index];
- for (uint32_t i = 0; i < num_pixels; ++i) {
- memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) +
- (i + num_pixels * color_index) *
- sizeof(color_texture)),
- &color_texture, sizeof(color_texture));
- }
- }
- uint32_t color_texture = color_textures[num_colors - 1];
- uint32_t num_colored_pixels = num_pixels * (num_colors - 1);
- num_pixels = width * height - num_colored_pixels;
- for (uint32_t i = 0; i < num_pixels; ++i) {
- memcpy(reinterpret_cast<void*>(reinterpret_cast<int64_t>(buffer_data) +
- (i + num_colored_pixels) *
- sizeof(color_texture)),
- &color_texture, sizeof(color_texture));
- }
- fence = -1;
- ret = AHardwareBuffer_unlock(ah_buffer, &fence);
- EXPECT_EQ(0, ret) << "Failed to unlock the hardware buffer.";
-
- // Release the android hardware buffer.
- AHardwareBuffer_release(ah_buffer);
-}
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
deleted file mode 100644
index 07e2121..0000000
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ /dev/null
@@ -1,891 +0,0 @@
-#include <android-base/properties.h>
-#include <base/logging.h>
-#include <cutils/properties.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-#include <poll.h>
-
-#include <android/hardware_buffer.h>
-
-#include <algorithm>
-#include <array>
-#include <set>
-#include <thread>
-#include <vector>
-
-#include <dvr/dvr_configuration_data.h>
-#include <dvr/dvr_deleter.h>
-#include <dvr/dvr_display_manager.h>
-#include <dvr/dvr_surface.h>
-
-#include <pdx/status.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-using ::testing::Test;
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int32_t value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT32;
- attribute.value.int32_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, int64_t value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_INT64;
- attribute.value.int64_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, bool value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_BOOL;
- attribute.value.bool_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, float value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT;
- attribute.value.float_value = value;
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 2>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2;
- std::copy(value.begin(), value.end(), attribute.value.float2_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 3>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3;
- std::copy(value.begin(), value.end(), attribute.value.float3_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 4>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4;
- std::copy(value.begin(), value.end(), attribute.value.float4_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 8>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8;
- std::copy(value.begin(), value.end(), attribute.value.float8_value);
- return attribute;
-}
-
-DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key,
- const std::array<float, 16>& value) {
- DvrSurfaceAttribute attribute;
- attribute.key = key;
- attribute.value.type = DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16;
- std::copy(value.begin(), value.end(), attribute.value.float16_value);
- return attribute;
-}
-
-Status<UniqueDvrSurface> CreateApplicationSurface(bool visible = false,
- int32_t z_order = 0) {
- DvrSurface* surface = nullptr;
- DvrSurfaceAttribute attributes[] = {
- MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, z_order),
- MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, visible)};
-
- const int ret = dvrSurfaceCreate(
- attributes, std::extent<decltype(attributes)>::value, &surface);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {UniqueDvrSurface(surface)};
-}
-
-Status<UniqueDvrWriteBufferQueue> CreateSurfaceQueue(
- const UniqueDvrSurface& surface, uint32_t width, uint32_t height,
- uint32_t format, uint32_t layer_count, uint64_t usage, size_t capacity,
- size_t metadata_size) {
- DvrWriteBufferQueue* queue;
- const int ret = dvrSurfaceCreateWriteBufferQueue(
- surface.get(), width, height, format, layer_count, usage, capacity,
- metadata_size, &queue);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {UniqueDvrWriteBufferQueue(queue)};
-}
-
-Status<std::vector<uint8_t>> GetConfigData(int config_type) {
- uint8_t* data = nullptr;
- size_t data_size = 0;
- int error = dvrConfigurationDataGet(config_type, &data, &data_size);
- if (error < 0) {
- return ErrorStatus(-error);
- }
-
- if (!data || data_size == 0) {
- return ErrorStatus(EINVAL);
- }
- std::vector<uint8_t> data_result(data, data + data_size);
- dvrConfigurationDataDestroy(data);
- std::string s(data, data + data_size);
- return {std::move(data_result)};
-}
-
-class TestDisplayManager {
- public:
- TestDisplayManager(UniqueDvrDisplayManager display_manager,
- UniqueDvrSurfaceState surface_state)
- : display_manager_(std::move(display_manager)),
- surface_state_(std::move(surface_state)) {
- const int fd = dvrDisplayManagerGetEventFd(display_manager_.get());
- LOG_IF(INFO, fd < 0) << "Failed to get event fd: " << strerror(-fd);
- display_manager_event_fd_ = fd;
- }
-
- Status<UniqueDvrReadBufferQueue> GetReadBufferQueue(int surface_id,
- int queue_id) {
- DvrReadBufferQueue* queue;
- const int ret = dvrDisplayManagerGetReadBufferQueue(
- display_manager_.get(), surface_id, queue_id, &queue);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {UniqueDvrReadBufferQueue(queue)};
- }
-
- Status<void> UpdateSurfaceState() {
- const int ret = dvrDisplayManagerGetSurfaceState(display_manager_.get(),
- surface_state_.get());
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {};
- }
-
- enum : int { kTimeoutMs = 10000 }; // 10s
-
- Status<void> WaitForUpdate(int timeout_ms = kTimeoutMs) {
- if (display_manager_event_fd_ < 0)
- return ErrorStatus(-display_manager_event_fd_);
-
- pollfd pfd = {display_manager_event_fd_, POLLIN, 0};
- const int count = poll(&pfd, 1, timeout_ms);
- if (count < 0)
- return ErrorStatus(errno);
- else if (count == 0)
- return ErrorStatus(ETIMEDOUT);
-
- int events;
- const int ret = dvrDisplayManagerTranslateEpollEventMask(
- display_manager_.get(), pfd.revents, &events);
- if (ret < 0)
- return ErrorStatus(-ret);
- else if (events & POLLIN)
- return UpdateSurfaceState();
- else
- return ErrorStatus(EPROTO);
- }
-
- Status<size_t> GetSurfaceCount() {
- size_t count = 0;
- const int ret =
- dvrSurfaceStateGetSurfaceCount(surface_state_.get(), &count);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {count};
- }
-
- Status<DvrSurfaceUpdateFlags> GetUpdateFlags(size_t surface_index) {
- DvrSurfaceUpdateFlags update_flags;
- const int ret = dvrSurfaceStateGetUpdateFlags(surface_state_.get(),
- surface_index, &update_flags);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {update_flags};
- }
-
- Status<int> GetSurfaceId(size_t surface_index) {
- int surface_id;
- const int ret = dvrSurfaceStateGetSurfaceId(surface_state_.get(),
- surface_index, &surface_id);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {surface_id};
- }
-
- Status<int> GetProcessId(size_t surface_index) {
- int process_id;
- const int ret = dvrSurfaceStateGetProcessId(surface_state_.get(),
- surface_index, &process_id);
- if (ret < 0)
- return ErrorStatus(-ret);
- else
- return {process_id};
- }
-
- Status<std::vector<DvrSurfaceAttribute>> GetAttributes(size_t surface_index) {
- std::vector<DvrSurfaceAttribute> attributes;
- size_t count = 0;
- const int ret = dvrSurfaceStateGetAttributeCount(surface_state_.get(),
- surface_index, &count);
- if (ret < 0)
- return ErrorStatus(-ret);
-
- attributes.resize(count);
- const ssize_t return_count = dvrSurfaceStateGetAttributes(
- surface_state_.get(), surface_index, attributes.data(), count);
- if (return_count < 0)
- return ErrorStatus(-return_count);
-
- attributes.resize(return_count);
- return {std::move(attributes)};
- }
-
- Status<std::vector<int>> GetQueueIds(size_t surface_index) {
- std::vector<int> queue_ids;
- size_t count = 0;
- const int ret = dvrSurfaceStateGetQueueCount(surface_state_.get(),
- surface_index, &count);
- if (ret < 0)
- return ErrorStatus(-ret);
-
- if (count > 0) {
- queue_ids.resize(count);
- const ssize_t return_count = dvrSurfaceStateGetQueueIds(
- surface_state_.get(), surface_index, queue_ids.data(), count);
- if (return_count < 0)
- return ErrorStatus(-return_count);
-
- queue_ids.resize(return_count);
- }
-
- return {std::move(queue_ids)};
- }
-
- private:
- UniqueDvrDisplayManager display_manager_;
- UniqueDvrSurfaceState surface_state_;
-
- // Owned by object in display_manager_, do not explicitly close.
- int display_manager_event_fd_;
-
- TestDisplayManager(const TestDisplayManager&) = delete;
- void operator=(const TestDisplayManager&) = delete;
-};
-
-class DvrDisplayManagerTest : public Test {
- protected:
- void SetUp() override {
- int ret;
- DvrDisplayManager* display_manager;
- DvrSurfaceState* surface_state;
-
- ret = dvrDisplayManagerCreate(&display_manager);
- ASSERT_EQ(0, ret) << "Failed to create display manager client";
- ASSERT_NE(nullptr, display_manager);
-
- ret = dvrSurfaceStateCreate(&surface_state);
- ASSERT_EQ(0, ret) << "Failed to create surface state object";
- ASSERT_NE(nullptr, surface_state);
-
- manager_.reset(
- new TestDisplayManager(UniqueDvrDisplayManager(display_manager),
- UniqueDvrSurfaceState(surface_state)));
- }
- void TearDown() override {}
-
- std::unique_ptr<TestDisplayManager> manager_;
-};
-
-// TODO(eieio): Consider moving these somewhere more central because they are
-// broadly useful.
-
-template <typename T>
-testing::AssertionResult StatusOk(const char* status_expression,
- const Status<T>& status) {
- if (!status.ok()) {
- return testing::AssertionFailure()
- << "(" << status_expression
- << ") expected to indicate success but actually contains error ("
- << status.error() << ")";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T>
-testing::AssertionResult StatusError(const char* status_expression,
- const Status<T>& status) {
- if (status.ok()) {
- return testing::AssertionFailure()
- << "(" << status_expression
- << ") expected to indicate error but instead indicates success.";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T>
-testing::AssertionResult StatusHasError(const char* status_expression,
- const char* /*error_code_expression*/,
- const Status<T>& status,
- int error_code) {
- if (status.ok()) {
- return StatusError(status_expression, status);
- } else if (status.error() != error_code) {
- return testing::AssertionFailure()
- << "(" << status_expression << ") expected to indicate error ("
- << error_code << ") but actually indicates error (" << status.error()
- << ")";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T, typename U>
-testing::AssertionResult StatusHasValue(const char* status_expression,
- const char* /*value_expression*/,
- const Status<T>& status,
- const U& value) {
- if (!status.ok()) {
- return StatusOk(status_expression, status);
- } else if (status.get() != value) {
- return testing::AssertionFailure()
- << "(" << status_expression << ") expected to contain value ("
- << testing::PrintToString(value) << ") but actually contains value ("
- << testing::PrintToString(status.get()) << ")";
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-template <typename T, typename Op>
-testing::AssertionResult StatusPred(const char* status_expression,
- const char* pred_expression,
- const Status<T>& status, Op pred) {
- if (!status.ok()) {
- return StatusOk(status_expression, status);
- } else if (!pred(status.get())) {
- return testing::AssertionFailure()
- << status_expression << " value ("
- << testing::PrintToString(status.get())
- << ") failed to pass predicate " << pred_expression;
- } else {
- return testing::AssertionSuccess();
- }
-}
-
-#define ASSERT_STATUS_OK(status) ASSERT_PRED_FORMAT1(StatusOk, status)
-#define ASSERT_STATUS_ERROR(status) ASSERT_PRED_FORMAT1(StatusError, status)
-
-#define ASSERT_STATUS_ERROR_VALUE(value, status) \
- ASSERT_PRED_FORMAT2(StatusHasError, status, value)
-
-#define ASSERT_STATUS_EQ(value, status) \
- ASSERT_PRED_FORMAT2(StatusHasValue, status, value)
-
-#define EXPECT_STATUS_OK(status) EXPECT_PRED_FORMAT1(StatusOk, status)
-#define EXPECT_STATUS_ERROR(status) EXPECT_PRED_FORMAT1(StatusError, status)
-
-#define EXPECT_STATUS_ERROR_VALUE(value, status) \
- EXPECT_PRED_FORMAT2(StatusHasError, status, value)
-
-#define EXPECT_STATUS_EQ(value, status) \
- EXPECT_PRED_FORMAT2(StatusHasValue, status, value)
-
-#define EXPECT_STATUS_PRED(pred, status) \
- EXPECT_PRED_FORMAT2(StatusPred, status, pred)
-
-#if 0
-// Verify utility predicate/macro functionality. This section is commented out
-// because it is designed to fail in some cases to validate the helpers.
-TEST_F(Test, ExpectVoid) {
- Status<void> status_error{ErrorStatus{EINVAL}};
- Status<void> status_ok{};
-
- EXPECT_STATUS_ERROR(status_error);
- EXPECT_STATUS_ERROR(status_ok);
- EXPECT_STATUS_OK(status_error);
- EXPECT_STATUS_OK(status_ok);
-
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
-}
-
-TEST_F(Test, ExpectInt) {
- Status<int> status_error{ErrorStatus{EINVAL}};
- Status<int> status_ok{10};
-
- EXPECT_STATUS_ERROR(status_error);
- EXPECT_STATUS_ERROR(status_ok);
- EXPECT_STATUS_OK(status_error);
- EXPECT_STATUS_OK(status_ok);
-
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_error);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_error);
- EXPECT_STATUS_ERROR_VALUE(EINVAL, status_ok);
- EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
-
- EXPECT_STATUS_EQ(10, status_error);
- EXPECT_STATUS_EQ(20, status_error);
- EXPECT_STATUS_EQ(10, status_ok);
- EXPECT_STATUS_EQ(20, status_ok);
-
- auto pred1 = [](const auto& value) { return value < 15; };
- auto pred2 = [](const auto& value) { return value > 5; };
- auto pred3 = [](const auto& value) { return value > 15; };
- auto pred4 = [](const auto& value) { return value < 5; };
-
- EXPECT_STATUS_PRED(pred1, status_error);
- EXPECT_STATUS_PRED(pred2, status_error);
- EXPECT_STATUS_PRED(pred3, status_error);
- EXPECT_STATUS_PRED(pred4, status_error);
- EXPECT_STATUS_PRED(pred1, status_ok);
- EXPECT_STATUS_PRED(pred2, status_ok);
- EXPECT_STATUS_PRED(pred3, status_ok);
- EXPECT_STATUS_PRED(pred4, status_ok);
-}
-#endif
-
-TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
- // Get surface state and verify there are no surfaces.
- ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
- ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
-
- // Get flags for invalid surface index.
- EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetUpdateFlags(0));
-
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- const int surface_id = dvrSurfaceGetId(surface.get());
- ASSERT_GE(surface_id, 0);
-
- // Now there should be one new surface.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify the new surface flag is set.
- auto check_flags = [](const auto& value) {
- return value & DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE;
- };
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- // Verify the surface id matches.
- EXPECT_STATUS_EQ(surface_id, manager_->GetSurfaceId(0));
-
- // Verify the owning process of the surface.
- EXPECT_STATUS_EQ(getpid(), manager_->GetProcessId(0));
-
- surface.reset();
-
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_EQ(0u, manager_->GetSurfaceCount());
-}
-
-TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
- // Get surface state and verify there are no surfaces.
- ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
- ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
-
- // Get attributes for an invalid surface index.
- EXPECT_STATUS_ERROR_VALUE(EINVAL, manager_->GetAttributes(0));
-
- const bool kInitialVisibility = true;
- const int32_t kInitialZOrder = 10;
- auto surface_status =
- CreateApplicationSurface(kInitialVisibility, kInitialZOrder);
- ASSERT_STATUS_OK(surface_status);
- auto surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Check the initial attribute values.
- auto attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- auto attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- std::set<int32_t> actual_keys;
- std::set<int32_t> expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- DVR_SURFACE_ATTRIBUTE_VISIBLE};
-
- // Collect all the keys in attributes that match the expected keys.
- auto compare_keys = [](const auto& attributes, const auto& expected_keys) {
- std::set<int32_t> keys;
- for (const auto& attribute : attributes) {
- if (expected_keys.find(attribute.key) != expected_keys.end())
- keys.emplace(attribute.key);
- }
- return keys;
- };
-
- // If the sets match then attributes contained at least the expected keys,
- // even if other keys were also present.
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-
- std::vector<DvrSurfaceAttribute> attributes_to_set = {
- MakeAttribute(DVR_SURFACE_ATTRIBUTE_Z_ORDER, 0)};
-
- // Test invalid args.
- EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(nullptr, attributes_to_set.data(),
- attributes_to_set.size()));
- EXPECT_EQ(-EINVAL, dvrSurfaceSetAttributes(surface.get(), nullptr,
- attributes_to_set.size()));
-
- // Test attribute change events.
- ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
-
- // Verify the attributes changed flag is set.
- auto check_flags = [](const auto& value) {
- return value & DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED;
- };
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- DVR_SURFACE_ATTRIBUTE_VISIBLE};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-
- // Test setting and then deleting an attribute.
- const DvrSurfaceAttributeKey kUserKey = 1;
- attributes_to_set = {MakeAttribute(kUserKey, 1024)};
-
- ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
- kUserKey};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-
- // Delete the attribute.
- attributes_to_set = {MakeAttribute(kUserKey, nullptr)};
-
- ASSERT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER, DVR_SURFACE_ATTRIBUTE_VISIBLE,
- kUserKey};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_NE(expected_keys, actual_keys);
-
- // Test deleting a reserved attribute.
- attributes_to_set = {MakeAttribute(DVR_SURFACE_ATTRIBUTE_VISIBLE, nullptr)};
-
- EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
-
- // Failed attribute operations should not trigger update events.
- const int kTimeoutMs = 100; // 0.1s
- EXPECT_STATUS_ERROR_VALUE(ETIMEDOUT, manager_->WaitForUpdate(kTimeoutMs));
-
- attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), 2u);
-
- expected_keys = {DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- DVR_SURFACE_ATTRIBUTE_VISIBLE};
-
- actual_keys.clear();
- actual_keys = compare_keys(attributes, expected_keys);
- EXPECT_EQ(expected_keys, actual_keys);
-}
-
-TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) {
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- enum : std::int32_t {
- kInt32Key = 1,
- kInt64Key,
- kBoolKey,
- kFloatKey,
- kFloat2Key,
- kFloat3Key,
- kFloat4Key,
- kFloat8Key,
- kFloat16Key,
- };
-
- const std::vector<DvrSurfaceAttribute> attributes_to_set = {
- MakeAttribute(kInt32Key, int32_t{0}),
- MakeAttribute(kInt64Key, int64_t{0}),
- MakeAttribute(kBoolKey, false),
- MakeAttribute(kFloatKey, 0.0f),
- MakeAttribute(kFloat2Key, std::array<float, 2>{{1.0f, 2.0f}}),
- MakeAttribute(kFloat3Key, std::array<float, 3>{{3.0f, 4.0f, 5.0f}}),
- MakeAttribute(kFloat4Key, std::array<float, 4>{{6.0f, 7.0f, 8.0f, 9.0f}}),
- MakeAttribute(kFloat8Key,
- std::array<float, 8>{{10.0f, 11.0f, 12.0f, 13.0f, 14.0f,
- 15.0f, 16.0f, 17.0f}}),
- MakeAttribute(kFloat16Key, std::array<float, 16>{
- {18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f,
- 24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f,
- 30.0f, 31.0f, 32.0f, 33.0f}})};
-
- EXPECT_EQ(0, dvrSurfaceSetAttributes(surface.get(), attributes_to_set.data(),
- attributes_to_set.size()));
-
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- auto attribute_status = manager_->GetAttributes(0);
- ASSERT_STATUS_OK(attribute_status);
- auto attributes = attribute_status.take();
- EXPECT_GE(attributes.size(), attributes_to_set.size());
-
- auto HasAttribute = [](const auto& attributes,
- DvrSurfaceAttributeKey key) -> bool {
- for (const auto& attribute : attributes) {
- if (attribute.key == key)
- return true;
- }
- return false;
- };
- auto AttributeType =
- [](const auto& attributes,
- DvrSurfaceAttributeKey key) -> DvrSurfaceAttributeType {
- for (const auto& attribute : attributes) {
- if (attribute.key == key)
- return attribute.value.type;
- }
- return DVR_SURFACE_ATTRIBUTE_TYPE_NONE;
- };
-
- ASSERT_TRUE(HasAttribute(attributes, kInt32Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT32,
- AttributeType(attributes, kInt32Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kInt64Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_INT64,
- AttributeType(attributes, kInt64Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kBoolKey));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_BOOL,
- AttributeType(attributes, kBoolKey));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloatKey));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT,
- AttributeType(attributes, kFloatKey));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat2Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT2,
- AttributeType(attributes, kFloat2Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat3Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT3,
- AttributeType(attributes, kFloat3Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat4Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT4,
- AttributeType(attributes, kFloat4Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat8Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT8,
- AttributeType(attributes, kFloat8Key));
-
- ASSERT_TRUE(HasAttribute(attributes, kFloat16Key));
- EXPECT_EQ(DVR_SURFACE_ATTRIBUTE_TYPE_FLOAT16,
- AttributeType(attributes, kFloat16Key));
-}
-
-TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- const int surface_id = dvrSurfaceGetId(surface.get());
- ASSERT_GE(surface_id, 0);
- // Get surface state and verify there is one surface.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify there are no queues for the surface recorded in the state
- // snapshot.
- EXPECT_STATUS_EQ(std::vector<int>{}, manager_->GetQueueIds(0));
-
- // Create a new queue in the surface.
- auto write_queue_status = CreateSurfaceQueue(
- surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, 1,
- AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0);
- ASSERT_STATUS_OK(write_queue_status);
- UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
- ASSERT_NE(nullptr, write_queue.get());
-
- const int queue_id = dvrWriteBufferQueueGetId(write_queue.get());
- ASSERT_GE(queue_id, 0);
-
- // Update surface state.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify the buffers changed flag is set.
- auto check_flags = [](const auto& value) {
- return value & DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED;
- };
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- auto queue_ids_status = manager_->GetQueueIds(0);
- ASSERT_STATUS_OK(queue_ids_status);
-
- auto queue_ids = queue_ids_status.take();
- ASSERT_EQ(1u, queue_ids.size());
- EXPECT_EQ(queue_id, queue_ids[0]);
-
- auto read_queue_status = manager_->GetReadBufferQueue(surface_id, queue_id);
- ASSERT_STATUS_OK(read_queue_status);
- UniqueDvrReadBufferQueue read_queue = read_queue_status.take();
- ASSERT_NE(nullptr, read_queue.get());
- EXPECT_EQ(queue_id, dvrReadBufferQueueGetId(read_queue.get()));
-
- write_queue.reset();
-
- // Verify that destroying the queue generates a surface update event.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Verify that the buffers changed flag is set.
- EXPECT_STATUS_PRED(check_flags, manager_->GetUpdateFlags(0));
-
- // Verify that the queue ids reflect the change.
- queue_ids_status = manager_->GetQueueIds(0);
- ASSERT_STATUS_OK(queue_ids_status);
-
- queue_ids = queue_ids_status.take();
- ASSERT_EQ(0u, queue_ids.size());
-}
-
-TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
- // Create an application surface.
- auto surface_status = CreateApplicationSurface();
- ASSERT_STATUS_OK(surface_status);
- UniqueDvrSurface surface = surface_status.take();
- ASSERT_NE(nullptr, surface.get());
-
- // Get surface state and verify there is one surface.
- ASSERT_STATUS_OK(manager_->WaitForUpdate());
- ASSERT_STATUS_EQ(1u, manager_->GetSurfaceCount());
-
- // Create a new queue in the surface.
- const uint32_t kLayerCount = 3;
- auto write_queue_status = CreateSurfaceQueue(
- surface, 320, 240, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, kLayerCount,
- AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, 1, 0);
- ASSERT_STATUS_OK(write_queue_status);
- UniqueDvrWriteBufferQueue write_queue = write_queue_status.take();
- ASSERT_NE(nullptr, write_queue.get());
-
- DvrWriteBuffer* buffer = nullptr;
- DvrNativeBufferMetadata metadata;
- int fence_fd = -1;
- int error = dvrWriteBufferQueueGainBuffer(write_queue.get(), /*timeout=*/1000,
- &buffer, &metadata, &fence_fd);
- ASSERT_EQ(0, error);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- error = dvrWriteBufferGetAHardwareBuffer(buffer, &hardware_buffer);
- ASSERT_EQ(0, error);
-
- AHardwareBuffer_Desc desc = {};
- AHardwareBuffer_describe(hardware_buffer, &desc);
- ASSERT_EQ(kLayerCount, desc.layers);
-
- AHardwareBuffer_release(hardware_buffer);
- dvrWriteBufferDestroy(buffer);
-}
-
-TEST_F(Test, ConfigurationData) {
- // TODO(hendrikw): Move this test and GetConfigData helper function out of the
- // display manager tests.
- auto data1 = GetConfigData(-1);
- ASSERT_STATUS_ERROR(data1);
-
- const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
-
- // This should be run on devices with and without built in metrics.
- bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty();
- auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
- if (has_metric) {
- ASSERT_STATUS_OK(data2);
- ASSERT_NE(0u, data2.get().size());
- } else {
- ASSERT_STATUS_ERROR(data2);
- }
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp b/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
deleted file mode 100644
index 5c837e7..0000000
--- a/libs/vr/libdvr/tests/dvr_named_buffer-test.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-#include <android/hardware_buffer.h>
-#include <dvr/dvr_buffer.h>
-#include <dvr/dvr_config.h>
-#include <dvr/dvr_shared_buffers.h>
-#include <dvr/dvr_surface.h>
-#include <system/graphics.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-TEST(DvrGlobalBufferTest, TestGlobalBuffersSameName) {
- const DvrGlobalBufferKey buffer_key = 101;
- DvrBuffer* buffer1 = nullptr;
- int ret1 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer1);
- ASSERT_EQ(0, ret1);
- ASSERT_NE(nullptr, buffer1);
-
- DvrBuffer* buffer2 = nullptr;
- int ret2 = dvrSetupGlobalBuffer(buffer_key, 10, 0, &buffer2);
- ASSERT_EQ(0, ret2);
- ASSERT_NE(nullptr, buffer2);
-
- AHardwareBuffer* hardware_buffer1 = nullptr;
- int e1 = dvrBufferGetAHardwareBuffer(buffer1, &hardware_buffer1);
- ASSERT_EQ(0, e1);
- ASSERT_NE(nullptr, hardware_buffer1);
-
- AHardwareBuffer* hardware_buffer2 = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(buffer2, &hardware_buffer2);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer2);
-
- AHardwareBuffer_Desc desc1 = {};
- AHardwareBuffer_describe(hardware_buffer1, &desc1);
- AHardwareBuffer_Desc desc2 = {};
- AHardwareBuffer_describe(hardware_buffer2, &desc2);
- ASSERT_EQ(desc1.width, 10u);
- ASSERT_EQ(desc1.height, 1u);
- ASSERT_EQ(desc1.layers, 1u);
- ASSERT_EQ(desc1.format, HAL_PIXEL_FORMAT_BLOB);
- ASSERT_EQ(desc1.usage, 0u);
- ASSERT_EQ(desc2.width, 10u);
- ASSERT_EQ(desc2.height, 1u);
- ASSERT_EQ(desc2.layers, 1u);
- ASSERT_EQ(desc2.format, HAL_PIXEL_FORMAT_BLOB);
- ASSERT_EQ(desc2.usage, 0u);
-
- dvrBufferDestroy(buffer1);
- dvrBufferDestroy(buffer2);
-
- DvrBuffer* buffer3 = nullptr;
- int e3 = dvrGetGlobalBuffer(buffer_key, &buffer3);
- ASSERT_NE(nullptr, buffer3);
- ASSERT_EQ(0, e3);
-
- AHardwareBuffer* hardware_buffer3 = nullptr;
- int e4 = dvrBufferGetAHardwareBuffer(buffer3, &hardware_buffer3);
- ASSERT_EQ(0, e4);
- ASSERT_NE(nullptr, hardware_buffer3);
-
- AHardwareBuffer_Desc desc3 = {};
- AHardwareBuffer_describe(hardware_buffer3, &desc3);
- ASSERT_EQ(desc3.width, 10u);
- ASSERT_EQ(desc3.height, 1u);
- ASSERT_EQ(desc3.layers, 1u);
- ASSERT_EQ(desc3.format, HAL_PIXEL_FORMAT_BLOB);
- ASSERT_EQ(desc3.usage, 0u);
-
- dvrBufferDestroy(buffer3);
-
- AHardwareBuffer_release(hardware_buffer1);
- AHardwareBuffer_release(hardware_buffer2);
- AHardwareBuffer_release(hardware_buffer3);
-}
-
-TEST(DvrGlobalBufferTest, TestMultipleGlobalBuffers) {
- const DvrGlobalBufferKey buffer_key1 = 102;
- const DvrGlobalBufferKey buffer_key2 = 103;
- DvrBuffer* setup_buffer1 = nullptr;
- int ret1 = dvrSetupGlobalBuffer(buffer_key1, 10, 0, &setup_buffer1);
- ASSERT_EQ(0, ret1);
- ASSERT_NE(nullptr, setup_buffer1);
- dvrBufferDestroy(setup_buffer1);
-
- DvrBuffer* setup_buffer2 = nullptr;
- int ret2 = dvrSetupGlobalBuffer(buffer_key2, 10, 0, &setup_buffer2);
- ASSERT_EQ(0, ret2);
- ASSERT_NE(nullptr, setup_buffer2);
- dvrBufferDestroy(setup_buffer2);
-
- DvrBuffer* buffer1 = nullptr;
- int e1 = dvrGetGlobalBuffer(buffer_key1, &buffer1);
- ASSERT_NE(nullptr, buffer1);
- ASSERT_EQ(0, e1);
- dvrBufferDestroy(buffer1);
-
- DvrBuffer* buffer2 = nullptr;
- int e2 = dvrGetGlobalBuffer(buffer_key2, &buffer2);
- ASSERT_NE(nullptr, buffer2);
- ASSERT_EQ(0, e2);
- dvrBufferDestroy(buffer2);
-}
-
-TEST(DvrGlobalBufferTest, TestGlobalBufferUsage) {
- const DvrGlobalBufferKey buffer_key = 100;
-
- // Set usage to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE. We use this because
- // internally AHARDWAREBUFFER_USAGE_VIDEO_ENCODE is converted to
- // GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER, and these two values are different.
- // If all is good, when we get the AHardwareBuffer, it should be converted
- // back to AHARDWAREBUFFER_USAGE_VIDEO_ENCODE.
- const uint64_t usage = AHARDWAREBUFFER_USAGE_VIDEO_ENCODE;
-
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_key, 10, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- AHardwareBuffer_Desc desc = {};
- AHardwareBuffer_describe(hardware_buffer, &desc);
- ASSERT_EQ(usage, desc.usage);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
-}
-
-TEST(DvrGlobalBufferTest, TestGlobalBufferCarriesData) {
- const DvrGlobalBufferKey buffer_name = 110;
-
- uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- constexpr size_t size = 1024 * sizeof(uint64_t);
- constexpr uint64_t value = 0x123456787654321;
-
- {
- // Allocate some data and set it to something.
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- void* buffer;
- int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
- ASSERT_EQ(0, e3);
- ASSERT_NE(nullptr, buffer);
- // Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
-
- uint64_t* data = static_cast<uint64_t*>(buffer);
- constexpr size_t num_values = size / sizeof(uint64_t);
- for (size_t i = 0; i < num_values; ++i) {
- data[i] = value;
- }
-
- int32_t fence = -1;
- int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
- ASSERT_EQ(0, e4);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
- }
-
- {
- // Get the buffer and check that all the data is still present.
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrGetGlobalBuffer(buffer_name, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- void* buffer;
- int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
- ASSERT_EQ(0, e3);
- ASSERT_NE(nullptr, buffer);
- // Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
-
- uint64_t* data = static_cast<uint64_t*>(buffer);
- constexpr size_t num_values = size / sizeof(uint64_t);
- bool is_equal = true;
- for (size_t i = 0; i < num_values; ++i) {
- is_equal &= (data[i] == value);
- }
- ASSERT_TRUE(is_equal);
-
- int32_t fence = -1;
- int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
- ASSERT_EQ(0, e4);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
- }
-}
-
-TEST(DvrGlobalBufferTest, TestGlobalBufferZeroed) {
- const DvrGlobalBufferKey buffer_name = 120;
-
- // Allocate 1MB and check that it is all zeros.
- uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- constexpr size_t size = 1024 * 1024;
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_name, size, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e1);
-
- AHardwareBuffer* hardware_buffer = nullptr;
- int e2 = dvrBufferGetAHardwareBuffer(setup_buffer, &hardware_buffer);
- ASSERT_EQ(0, e2);
- ASSERT_NE(nullptr, hardware_buffer);
-
- void* buffer;
- int e3 = AHardwareBuffer_lock(hardware_buffer, usage, -1, nullptr, &buffer);
- ASSERT_EQ(0, e3);
- ASSERT_NE(nullptr, buffer);
- // Verify that the buffer pointer is at least 16 byte aligned.
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(buffer) & (16 - 1));
-
- uint64_t* data = static_cast<uint64_t*>(buffer);
- constexpr size_t num_values = size / sizeof(uint64_t);
- uint64_t zero = 0;
- for (size_t i = 0; i < num_values; ++i) {
- zero |= data[i];
- }
- ASSERT_EQ(0U, zero);
-
- int32_t fence = -1;
- int e4 = AHardwareBuffer_unlock(hardware_buffer, &fence);
- ASSERT_EQ(0, e4);
-
- dvrBufferDestroy(setup_buffer);
- AHardwareBuffer_release(hardware_buffer);
-}
-
-TEST(DvrGlobalBufferTest, TestVrflingerConfigBuffer) {
- const DvrGlobalBufferKey buffer_name =
- DvrGlobalBuffers::kVrFlingerConfigBufferKey;
-
- // First delete any existing buffer so we can test the failure case.
- dvrDeleteGlobalBuffer(buffer_name);
-
- const uint64_t usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY;
-
- size_t correct_size = DvrConfigRing::MemorySize();
- size_t wrong_size = DvrConfigRing::MemorySize(0);
-
- // Setup an invalid config buffer (too small) and assert that it fails.
- DvrBuffer* setup_buffer = nullptr;
- int e1 = dvrSetupGlobalBuffer(buffer_name, wrong_size, usage, &setup_buffer);
- ASSERT_EQ(nullptr, setup_buffer);
- ASSERT_GT(0, e1);
-
- // Setup a correct config buffer.
- int e2 =
- dvrSetupGlobalBuffer(buffer_name, correct_size, usage, &setup_buffer);
- ASSERT_NE(nullptr, setup_buffer);
- ASSERT_EQ(0, e2);
-
- dvrBufferDestroy(setup_buffer);
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp
deleted file mode 100644
index 3b6d6e1..0000000
--- a/libs/vr/libdvr/tests/dvr_tracking-test.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include <android/log.h>
-#include <gtest/gtest.h>
-
-#include "dvr_api_test.h"
-
-namespace {
-
-class DvrTrackingTest : public DvrApiTest {};
-
-#if DVR_TRACKING_IMPLEMENTED
-
-TEST_F(DvrTrackingTest, Implemented) {
- ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
-
- ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
-}
-
-TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) {
- int ret;
- ret = api_.TrackingCameraCreate(nullptr);
- EXPECT_EQ(ret, -EINVAL);
-
- DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42);
- ret = api_.TrackingCameraCreate(&camera);
- EXPECT_EQ(ret, -EINVAL);
-}
-
-TEST_F(DvrTrackingTest, CameraCreateDestroy) {
- DvrTrackingCamera* camera = nullptr;
- int ret = api_.TrackingCameraCreate(&camera);
-
- EXPECT_EQ(ret, 0);
- ASSERT_TRUE(camera != nullptr);
-
- api_.TrackingCameraDestroy(camera);
-}
-
-TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) {
- int ret;
- ret = api_.TrackingFeatureExtractorCreate(nullptr);
- EXPECT_EQ(ret, -EINVAL);
-
- DvrTrackingFeatureExtractor* camera =
- reinterpret_cast<DvrTrackingFeatureExtractor*>(42);
- ret = api_.TrackingFeatureExtractorCreate(&camera);
- EXPECT_EQ(ret, -EINVAL);
-}
-
-TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) {
- DvrTrackingFeatureExtractor* camera = nullptr;
- int ret = api_.TrackingFeatureExtractorCreate(&camera);
-
- EXPECT_EQ(ret, 0);
- ASSERT_TRUE(camera != nullptr);
-
- api_.TrackingFeatureExtractorDestroy(camera);
-}
-
-#else // !DVR_TRACKING_IMPLEMENTED
-
-TEST_F(DvrTrackingTest, NotImplemented) {
- ASSERT_TRUE(api_.TrackingCameraCreate != nullptr);
- ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStart != nullptr);
- ASSERT_TRUE(api_.TrackingCameraStop != nullptr);
-
- EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS);
-
- ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr);
- ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr);
-
- EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr),
- -ENOSYS);
- EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr,
- nullptr, nullptr),
- -ENOSYS);
-
- ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr);
- ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr);
- ASSERT_TRUE(api_.TrackingSensorsStart != nullptr);
- ASSERT_TRUE(api_.TrackingSensorsStop != nullptr);
-
- EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS);
- EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS);
-}
-
-#endif // DVR_TRACKING_IMPLEMENTED
-
-} // namespace
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
deleted file mode 100644
index 40a5099..0000000
--- a/libs/vr/libvrsensor/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2015 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"],
-}
-
-sourceFiles = [
- "pose_client.cpp",
- "latency_model.cpp",
-]
-
-includeFiles = [
- "include",
-]
-
-staticLibraries = [
- "libdisplay",
- "libdvrcommon",
- "libbroadcastring",
-]
-
-sharedLibraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libhardware",
- "liblog",
- "libutils",
- "libui",
- "libpdx_default_transport",
-]
-
-cc_library {
- srcs: sourceFiles,
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-macro-redefined",
- ],
- export_include_dirs: includeFiles,
- static_libs: staticLibraries,
- shared_libs: sharedLibraries,
- header_libs: ["libdvr_headers"],
- name: "libvrsensor",
-}
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libvrsensor/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
deleted file mode 100644
index b663a67..0000000
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ /dev/null
@@ -1,176 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_H_
-#define ANDROID_DVR_POSE_CLIENT_H_
-
-#ifdef __ARM_NEON
-#include <arm_neon.h>
-#else
-#ifndef __FLOAT32X4T_86
-#define __FLOAT32X4T_86
-typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
-#endif
-#endif
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <dvr/dvr_pose.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-// Returned by the async pose ring buffer access API.
-typedef struct DvrPoseRingBufferInfo {
- // Read-only pointer to the pose ring buffer. The current pose is in this
- // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
- // frame's forecasted pose is at element
- // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
- // predicted for when 50% of the corresponding frame's pixel data is visible
- // to the user.
- // The last value returned by dvrPresent is the count for the next frame,
- // which is the earliest that the application could display something if they
- // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
- volatile const DvrPoseAsync* buffer;
- // Minimum number of accurate forecasted poses including the current frame's
- // pose. This is the number of poses that are udpated by the pose service.
- // If the application reads past this count, they will get a stale prediction
- // from a previous frame. Guaranteed to be at least 2.
- uint32_t min_future_count;
- // Number of elements in buffer. At least 8 and greater than min_future_count.
- // Guaranteed to be a power of two. The total size of the buffer in bytes is:
- // total_count * sizeof(DvrPoseAsync)
- uint32_t total_count;
-} DvrPoseRingBufferInfo;
-
-typedef enum DvrPoseMode {
- DVR_POSE_MODE_6DOF = 0,
- DVR_POSE_MODE_3DOF,
- DVR_POSE_MODE_MOCK_FROZEN,
- DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
- DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
- DVR_POSE_MODE_MOCK_ROTATE_SLOW,
- DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
- DVR_POSE_MODE_MOCK_ROTATE_FAST,
- DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
- DVR_POSE_MODE_FLOAT,
- DVR_POSE_MODE_MOCK_MOTION_SICKNESS,
-
- // Always last.
- DVR_POSE_MODE_COUNT,
-} DvrPoseMode;
-
-typedef enum DvrControllerId {
- DVR_CONTROLLER_0 = 0,
- DVR_CONTROLLER_1 = 1,
-} DvrControllerId;
-
-// Creates a new pose client.
-//
-// @return Pointer to the created pose client, nullptr on failure.
-DvrPoseClient* dvrPoseClientCreate();
-
-// Destroys a pose client.
-//
-// @param client Pointer to the pose client to be destroyed.
-void dvrPoseClientDestroy(DvrPoseClient* client);
-
-// Gets the pose for the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-// Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
- DvrPoseAsync* out_pose);
-
-// Gets the current vsync count.
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client);
-
-// Gets the pose for the given controller at the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param controller_id The controller id.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-// Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
- uint32_t vsync_count, DvrPoseAsync* out_pose);
-
-// Enables/disables logging for the controller fusion.
-//
-// @param client Pointer to the pose client.
-// @param enable True starts logging, False stops.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable);
-
-// DEPRECATED
-// Polls current pose state.
-//
-// @param client Pointer to the pose client.
-// @param state Struct to store polled state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state);
-
-// Freezes the pose to the provided state.
-//
-// Future poll operations will return this state until a different state is
-// frozen or dvrPoseClientModeSet() is called with a different mode. The timestamp is
-// not frozen.
-//
-// @param client Pointer to the pose client.
-// @param frozen_state State pose to be frozen to.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state);
-
-// Sets the pose service mode.
-//
-// @param mode The requested pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode);
-
-// Gets the pose service mode.
-//
-// @param mode Return value for the current pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode);
-
-// Get access to the shared memory pose ring buffer.
-// A future pose at vsync <current> + <offset> is accessed at index:
-// index = (<current> + <offset>) % out_buffer_size
-// Where <current> was the last value returned by dvrPresent and
-// <offset> is less than or equal to |out_min_future_count|.
-// |out_buffer| will be set to a pointer to the buffer.
-// |out_fd| will be set to the gralloc buffer file descriptor, which is
-// required for binding this buffer for GPU use.
-// Returns 0 on success.
-int dvrPoseClientGetRingBuffer(DvrPoseClient* client,
- DvrPoseRingBufferInfo* out_info);
-
-// Sets enabled state for sensors pose processing.
-//
-// @param enabled Whether sensors are enabled or disabled.
-// @return Zero on success
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled);
-
-// Requests a burst of data samples from pose service. The data samples are
-// passed through a shared memory buffer obtained by calling
-// dvrPoseClientGetDataReader().
-//
-// @param DvrPoseDataCaptureRequest Parameters on how to capture data.
-// @return Zero on success.
-int dvrPoseClientDataCapture(DvrPoseClient* client,
- const DvrPoseDataCaptureRequest* request);
-
-// Destroys the write buffer queue for the given |data_type|.
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
deleted file mode 100644
index bf0e687..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_LATENCY_MODEL_H_
-#define ANDROID_DVR_LATENCY_MODEL_H_
-
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-// This class models the latency from sensors. It will look at the first
-// window_size measurements and return their average after that.
-class LatencyModel {
- public:
- explicit LatencyModel(size_t window_size);
- ~LatencyModel() = default;
-
- void AddLatency(int64_t latency_ns);
- int64_t CurrentLatencyEstimate() const { return latency_; }
-
- private:
- size_t window_size_;
- int64_t latency_sum_ = 0;
- size_t num_summed_ = 0;
- int64_t latency_ = 0;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_LATENCY_MODEL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
deleted file mode 100644
index 7bf1cd4..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_POSE_IPC_H_
-#define ANDROID_DVR_POSE_IPC_H_
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DVR_POSE_SERVICE_BASE "system/vr/pose"
-#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
-
-enum {
- DVR_POSE_FREEZE = 0,
- DVR_POSE_SET_MODE,
- DVR_POSE_GET_MODE,
- DVR_POSE_GET_CONTROLLER_RING_BUFFER,
- DVR_POSE_LOG_CONTROLLER,
- DVR_POSE_SENSORS_ENABLE,
- DVR_POSE_GET_TANGO_READER,
- DVR_POSE_DATA_CAPTURE,
- DVR_POSE_TANGO_READER_DESTROY,
-};
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
deleted file mode 100644
index 39592bb..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-
-#include <private/dvr/buffer_hub_queue_client.h>
-
-using android::dvr::ConsumerQueue;
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-namespace android {
-namespace dvr {
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
- ConsumerQueue **queue_out);
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
deleted file mode 100644
index d3a4521..0000000
--- a/libs/vr/libvrsensor/latency_model.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <private/dvr/latency_model.h>
-
-#include <cmath>
-
-namespace android {
-namespace dvr {
-
-LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
-
-void LatencyModel::AddLatency(int64_t latency_ns) {
- // Not enough samples yet?
- if (num_summed_ < window_size_) {
- // Accumulate.
- latency_sum_ += latency_ns;
-
- // Have enough samples for latency estimate?
- if (++num_summed_ == window_size_) {
- latency_ = latency_sum_ / window_size_;
- }
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
deleted file mode 100644
index 4ff6a09..0000000
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-#define LOG_TAG "PoseClient"
-#include <dvr/dvr_shared_buffers.h>
-#include <dvr/pose_client.h>
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <pdx/client.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/pose-ipc.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-using android::dvr::ConsumerQueue;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace {
-
-typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing;
-
-constexpr static int32_t MAX_CONTROLLERS = 2;
-} // namespace
-
-// PoseClient is a remote interface to the pose service in sensord.
-class PoseClient : public pdx::ClientBase<PoseClient> {
- public:
- ~PoseClient() override {}
-
- // Casts C handle into an instance of this class.
- static PoseClient* FromC(DvrPoseClient* client) {
- return reinterpret_cast<PoseClient*>(client);
- }
-
- // Polls the pose service for the current state and stores it in *state.
- // Returns zero on success, a negative error code otherwise.
- int Poll(DvrPose* state) {
- // Allocate the helper class to access the sensor pose buffer.
- if (sensor_pose_buffer_ == nullptr) {
- sensor_pose_buffer_ = std::make_unique<SensorPoseRing>(
- DvrGlobalBuffers::kSensorPoseBuffer, CPUUsageMode::READ_RARELY);
- }
-
- if (state) {
- if (sensor_pose_buffer_->GetNewest(state)) {
- return 0;
- } else {
- return -EAGAIN;
- }
- }
-
- return -EINVAL;
- }
-
- int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
- const auto vsync_buffer = GetVsyncBuffer();
- if (vsync_buffer) {
- *out_pose =
- vsync_buffer
- ->vsync_poses[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
- return 0;
- } else {
- return -EAGAIN;
- }
- }
-
- uint32_t GetVsyncCount() {
- const auto vsync_buffer = GetVsyncBuffer();
- if (vsync_buffer) {
- return vsync_buffer->vsync_count;
- }
-
- return 0;
- }
-
- int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
- DvrPoseAsync* out_pose) {
- if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
- return -EINVAL;
- }
- if (!controllers_[controller_id].mapped_pose_buffer) {
- int ret = GetControllerRingBuffer(controller_id);
- if (ret < 0)
- return ret;
- }
- *out_pose =
- controllers_[controller_id]
- .mapped_pose_buffer[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
- return 0;
- }
-
- int LogController(bool enable) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
- sizeof(enable), nullptr, 0);
- ALOGE_IF(!status, "Pose LogController() failed because: %s",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Freezes the pose to the provided state. Future poll operations will return
- // this state until a different state is frozen or SetMode() is called with a
- // different mode.
- // Returns zero on success, a negative error code otherwise.
- int Freeze(const DvrPose& frozen_state) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
- sizeof(frozen_state), nullptr, 0);
- ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Sets the data mode for the pose service.
- int SetMode(DvrPoseMode mode) {
- Transaction trans{*this};
- Status<int> status =
- trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
- ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Gets the data mode for the pose service.
- int GetMode(DvrPoseMode* out_mode) {
- int mode;
- Transaction trans{*this};
- Status<int> status =
- trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
- ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
- status.GetErrorMessage().c_str());
- if (status)
- *out_mode = DvrPoseMode(mode);
- return ReturnStatusOrError(status);
- }
-
- int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
- // Get buffer.
- Transaction trans{*this};
- Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
- DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0);
-
- if (!status) {
- ALOGE("PoseClient GetTangoReaderHandle() failed because: %s",
- status.GetErrorMessage().c_str());
- *queue_out = nullptr;
- return -status.error();
- }
-
- std::unique_ptr<ConsumerQueue> consumer_queue =
- ConsumerQueue::Import(status.take());
- *queue_out = consumer_queue.release();
- return 0;
- }
-
- int DataCapture(const DvrPoseDataCaptureRequest* request) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request,
- sizeof(*request), nullptr, 0);
- ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- int DataReaderDestroy(uint64_t data_type) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY,
- &data_type, sizeof(data_type), nullptr,
- 0);
- ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Enables or disables all pose processing from sensors
- int EnableSensors(bool enabled) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_SENSORS_ENABLE, &enabled,
- sizeof(enabled), nullptr, 0);
- ALOGE_IF(!status, "Pose EnableSensors() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
- // First time mapping the buffer?
- const auto vsync_buffer = GetVsyncBuffer();
- if (vsync_buffer) {
- if (out_info) {
- out_info->min_future_count = DvrVsyncPoseBuffer::kMinFutureCount;
- out_info->total_count = DvrVsyncPoseBuffer::kSize;
- out_info->buffer = vsync_buffer->vsync_poses;
- }
- return -EINVAL;
- }
-
- return -EAGAIN;
- }
-
- int GetControllerRingBuffer(int32_t controller_id) {
- if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
- return -EINVAL;
- }
- ControllerClientState& client_state = controllers_[controller_id];
- if (client_state.pose_buffer.get()) {
- return 0;
- }
-
- Transaction trans{*this};
- Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
- DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
- sizeof(controller_id), nullptr, 0);
- if (!status) {
- return -status.error();
- }
-
- auto buffer = ConsumerBuffer::Import(status.take());
- if (!buffer) {
- ALOGE("Pose failed to import ring buffer");
- return -EIO;
- }
- constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync);
- void* addr = nullptr;
- int ret = buffer->GetBlobReadWritePointer(size, &addr);
- if (ret < 0 || !addr) {
- ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
- return -EIO;
- }
- client_state.pose_buffer.swap(buffer);
- client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
- ALOGI(
- "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
- controller_id, client_state.mapped_pose_buffer[0].position[0],
- client_state.mapped_pose_buffer[0].position[1],
- client_state.mapped_pose_buffer[0].position[2],
- client_state.mapped_pose_buffer[0].orientation[0],
- client_state.mapped_pose_buffer[0].orientation[1],
- client_state.mapped_pose_buffer[0].orientation[2],
- client_state.mapped_pose_buffer[0].orientation[3]);
- return 0;
- }
-
- private:
- friend BASE;
-
- // Set up a channel to the pose service.
- PoseClient()
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- DVR_POSE_SERVICE_CLIENT)) {
- // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
- // block while waiting for the pose service to come back up.
- EnableAutoReconnect(kInfiniteTimeout);
- }
-
- PoseClient(const PoseClient&) = delete;
- PoseClient& operator=(const PoseClient&) = delete;
-
- const DvrVsyncPoseBuffer* GetVsyncBuffer() {
- if (mapped_vsync_pose_buffer_ == nullptr) {
- if (vsync_pose_buffer_ == nullptr) {
- // The constructor tries mapping it so we do not need TryMapping after.
- vsync_pose_buffer_ = std::make_unique<CPUMappedBuffer>(
- DvrGlobalBuffers::kVsyncPoseBuffer, CPUUsageMode::READ_OFTEN);
- } else if (vsync_pose_buffer_->IsMapped() == false) {
- vsync_pose_buffer_->TryMapping();
- }
-
- if (vsync_pose_buffer_->IsMapped()) {
- mapped_vsync_pose_buffer_ =
- static_cast<DvrVsyncPoseBuffer*>(vsync_pose_buffer_->Address());
- }
- }
-
- return mapped_vsync_pose_buffer_;
- }
-
- // The vsync pose buffer if already mapped.
- std::unique_ptr<CPUMappedBuffer> vsync_pose_buffer_;
-
- // The direct sensor pose buffer.
- std::unique_ptr<SensorPoseRing> sensor_pose_buffer_;
-
- const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr;
-
- struct ControllerClientState {
- std::unique_ptr<ConsumerBuffer> pose_buffer;
- const DvrPoseAsync* mapped_pose_buffer = nullptr;
- };
- ControllerClientState controllers_[MAX_CONTROLLERS];
-};
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
- ConsumerQueue** queue_out) {
- return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
-}
-
-} // namespace dvr
-} // namespace android
-
-using android::dvr::PoseClient;
-
-extern "C" {
-
-DvrPoseClient* dvrPoseClientCreate() {
- auto* client = PoseClient::Create().release();
- return reinterpret_cast<DvrPoseClient*>(client);
-}
-
-void dvrPoseClientDestroy(DvrPoseClient* client) {
- delete PoseClient::FromC(client);
-}
-
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
- DvrPoseAsync* out_pose) {
- return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
-}
-
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client) {
- return PoseClient::FromC(client)->GetVsyncCount();
-}
-
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
- uint32_t vsync_count, DvrPoseAsync* out_pose) {
- return PoseClient::FromC(client)->GetControllerPose(controller_id,
- vsync_count, out_pose);
-}
-
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable) {
- return PoseClient::FromC(client)->LogController(enable);
-}
-
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state) {
- return PoseClient::FromC(client)->Poll(state);
-}
-
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state) {
- return PoseClient::FromC(client)->Freeze(*frozen_state);
-}
-
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode) {
- return PoseClient::FromC(client)->SetMode(mode);
-}
-
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) {
- return PoseClient::FromC(client)->GetMode(mode);
-}
-
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) {
- return PoseClient::FromC(client)->EnableSensors(enabled);
-}
-
-int dvrPoseClientDataCapture(DvrPoseClient* client,
- const DvrPoseDataCaptureRequest* request) {
- return PoseClient::FromC(client)->DataCapture(request);
-}
-
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
- return PoseClient::FromC(client)->DataReaderDestroy(data_type);
-}
-
-} // extern "C"
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index b3d6f74..e0ba946 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -118,70 +118,27 @@
: "rax", "cc" \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
+ #define API_ENTRY(_api) __attribute__((noinline)) _api
- #define API_ENTRY(_api) __attribute__((noinline)) _api
-
- #define CALL_GL_EXTENSION_API(_api, ...) \
- register unsigned int _t0 asm("$12"); \
- register unsigned int _fn asm("$25"); \
- register unsigned int _tls asm("$3"); \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- "rdhwr %[tls], $29\n\t" \
- "ld %[t0], %[OPENGL_API](%[tls])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " move %[fn], $ra\n\t" \
- "ld %[t0], %[API](%[t0])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " nop\n\t" \
- "move %[fn], %[t0]\n\t" \
- "1:\n\t" \
- "jalr $0, %[fn]\n\t" \
- " nop\n\t" \
- ".set pop\n\t" \
- : [fn] "=c"(_fn), \
- [tls] "=&r"(_tls), \
- [t0] "=&r"(_t0) \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, \
- ext.extensions[_api])) \
- : \
- );
-
-#elif defined(__mips__)
-
- #define API_ENTRY(_api) __attribute__((noinline)) _api
-
- #define CALL_GL_EXTENSION_API(_api, ...) \
- register unsigned int _t0 asm("$8"); \
- register unsigned int _fn asm("$25"); \
- register unsigned int _tls asm("$3"); \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- ".set mips32r2\n\t" \
- "rdhwr %[tls], $29\n\t" \
- "lw %[t0], %[OPENGL_API](%[tls])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " move %[fn], $ra\n\t" \
- "lw %[t0], %[API](%[t0])\n\t" \
- "beqz %[t0], 1f\n\t" \
- " nop\n\t" \
- "move %[fn], %[t0]\n\t" \
- "1:\n\t" \
- "jalr $0, %[fn]\n\t" \
- " nop\n\t" \
- ".set pop\n\t" \
- : [fn] "=c"(_fn), \
- [tls] "=&r"(_tls), \
- [t0] "=&r"(_t0) \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, \
- ext.extensions[_api])) \
- : \
- );
+ #define CALL_GL_EXTENSION_API(_api) \
+ asm volatile( \
+ "mv t0, tp\n" \
+ "li t1, %[tls]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "beqz t0, 1f\n" \
+ "li t1, %[api]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "jalr x0, t0\n" \
+ "1: ret\n" \
+ : \
+ : [tls] "i" (TLS_SLOT_OPENGL_API * sizeof(void*)), \
+ [api] "i" (__builtin_offsetof(gl_hooks_t, \
+ ext.extensions[_api])) \
+ : "t0", "t1" \
+ );
#endif
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 65f50f5..5bd5c14 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -191,73 +191,44 @@
: \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
#define API_ENTRY(_api) __attribute__((naked,noinline)) _api
- // t0: $12
- // fn: $25
- // tls: $3
- // v0: $2
- #define CALL_GL_API_INTERNAL_CALL(_api, ...) \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- "rdhwr $3, $29\n\t" \
- "ld $12, %[OPENGL_API]($3)\n\t" \
- "beqz $12, 1f\n\t" \
- " move $25, $ra\n\t" \
- "ld $12, %[API]($12)\n\t" \
- "beqz $12, 1f\n\t" \
- " nop\n\t" \
- "move $25, $12\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
- : \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", \
- "$10", "$11", "$12", "$25" \
- );
-
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
-
-#elif defined(__mips__)
-
- #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
-
- // t0: $8
- // fn: $25
- // tls: $3
- // v0: $2
#define CALL_GL_API_INTERNAL_CALL(_api, ...) \
asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- ".set mips32r2\n\t" \
- "rdhwr $3, $29\n\t" \
- "lw $3, %[OPENGL_API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " move $25,$ra\n\t" \
- "lw $3, %[API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " nop\n\t" \
- "move $25, $3\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
+ "mv t0, tp\n" \
+ "li t1, %[tls]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "beqz t0, 1f\n" \
+ "li t1, %[api]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "jalr x0, t0\n" \
+ "1:\n" \
: \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25" \
+ : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \
+ [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \
+ : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \
+ "a5", "t6", "t3", "t4", "t5", "t6" \
);
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
+ #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+ asm volatile( \
+ "li a0, 0\n" \
+ : \
+ : \
+ : "a0" \
+ );
+
+ #define CALL_GL_API_INTERNAL_DO_RETURN \
+ asm volatile( \
+ "ret\n" \
+ : \
+ : \
+ : \
+ );
#endif
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index bacd4b4..64c0f97 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -247,73 +247,44 @@
: \
);
-#elif defined(__mips64)
+#elif defined(__riscv)
#define API_ENTRY(_api) __attribute__((naked,noinline)) _api
- // t0: $12
- // fn: $25
- // tls: $3
- // v0: $2
- #define CALL_GL_API_INTERNAL_CALL(_api, ...) \
- asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- "rdhwr $3, $29\n\t" \
- "ld $12, %[OPENGL_API]($3)\n\t" \
- "beqz $12, 1f\n\t" \
- " move $25, $ra\n\t" \
- "ld $12, %[API]($12)\n\t" \
- "beqz $12, 1f\n\t" \
- " nop\n\t" \
- "move $25, $12\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
- : \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*sizeof(void*)),\
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", \
- "$10", "$11", "$12", "$25" \
- );
-
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
-
-#elif defined(__mips__)
-
- #define API_ENTRY(_api) __attribute__((naked,noinline)) _api
-
- // t0: $8
- // fn: $25
- // tls: $3
- // v0: $2
#define CALL_GL_API_INTERNAL_CALL(_api, ...) \
asm volatile( \
- ".set push\n\t" \
- ".set noreorder\n\t" \
- ".set mips32r2\n\t" \
- "rdhwr $3, $29\n\t" \
- "lw $3, %[OPENGL_API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " move $25,$ra\n\t" \
- "lw $3, %[API]($3)\n\t" \
- "beqz $3, 1f\n\t" \
- " nop\n\t" \
- "move $25, $3\n\t" \
- "1:\n\t" \
- "jalr $0, $25\n\t" \
- " move $2, $0\n\t" \
- ".set pop\n\t" \
+ "mv t0, tp\n" \
+ "li t1, %[tls]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "beqz t0, 1f\n" \
+ "li t1, %[api]\n" \
+ "add t0, t0, t1\n" \
+ "ld t0, 0(t0)\n" \
+ "jalr x0, t0\n" \
+ "1:\n" \
: \
- : [OPENGL_API] "I"(TLS_SLOT_OPENGL_API*4), \
- [API] "I"(__builtin_offsetof(gl_hooks_t, gl._api)) \
- : "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$25" \
+ : [tls] "i"(TLS_SLOT_OPENGL_API*sizeof(void *)), \
+ [api] "i"(__builtin_offsetof(gl_hooks_t, gl._api)) \
+ : "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", \
+ "a5", "t6", "t3", "t4", "t5", "t6" \
);
- #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE
- #define CALL_GL_API_INTERNAL_DO_RETURN
+ #define CALL_GL_API_INTERNAL_SET_RETURN_VALUE \
+ asm volatile( \
+ "li a0, 0\n" \
+ : \
+ : \
+ : "a0" \
+ );
+
+ #define CALL_GL_API_INTERNAL_DO_RETURN \
+ asm volatile( \
+ "ret\n" \
+ : \
+ : \
+ : \
+ );
#endif
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index fd4522e..e94b565 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -36,7 +36,14 @@
return;
}
- const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ fprintf(stderr, "Failed to get ID for any displays.\n");
+ return;
+ }
+
+ // display 0 is picked for now, can extend to support all displays if needed
+ const auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
if (displayToken == nullptr) {
fprintf(stderr, "ERROR: no display\n");
return;
diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp
index 1e37991..9b78391 100644
--- a/services/batteryservice/Android.bp
+++ b/services/batteryservice/Android.bp
@@ -9,6 +9,7 @@
cc_library_headers {
name: "libbatteryservice_headers",
+ host_supported: true,
vendor_available: true,
recovery_available: true,
export_include_dirs: ["include"],
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ddcd51f..b885435 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -41,7 +41,9 @@
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
sanitize: {
- misc_undefined: ["bounds"],
+ misc_undefined: [
+ "bounds",
+ ],
},
tidy: true,
tidy_checks: [
@@ -57,11 +59,10 @@
filegroup {
name: "libinputflinger_sources",
srcs: [
- "InputProcessor.cpp",
"InputCommonConverter.cpp",
+ "InputProcessor.cpp",
"PreferStylusOverTouchBlocker.cpp",
"UnwantedInteractionBlocker.cpp",
- "InputManager.cpp",
],
}
@@ -77,13 +78,10 @@
"libcrypto",
"libcutils",
"libhidlbase",
- "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
"libstatslog",
- "libstatspull",
- "libstatssocket",
"libutils",
"server_configurable_flags",
],
@@ -92,6 +90,23 @@
"libpalmrejection",
"libui-types",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libgui",
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ },
}
cc_library_shared {
@@ -100,6 +115,10 @@
"inputflinger_defaults",
"libinputflinger_defaults",
],
+ srcs: [
+ "InputManager.cpp",
+ // other sources are added via "defaults"
+ ],
cflags: [
// TODO(b/23084678): Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
@@ -108,9 +127,8 @@
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
"libinputflinger_base",
- "libinputreporter",
"libinputreader",
- "libgui",
+ "libinputreporter",
],
static_libs: [
"libinputdispatcher",
@@ -130,6 +148,7 @@
cc_library_headers {
name: "libinputflinger_headers",
+ host_supported: true,
export_include_dirs: ["include"],
}
@@ -151,17 +170,30 @@
"libbase",
"libbinder",
"libcutils",
- "libinput",
"liblog",
"libutils",
],
header_libs: [
"libinputflinger_headers",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libinput",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libui-types",
+ ],
+ },
+ },
}
cc_library_shared {
name: "libinputflinger_base",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
"libinputflinger_base_defaults",
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 8aee39f..628ce6f 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -263,6 +263,11 @@
static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14);
static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15);
static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16);
+// TODO(hcutts): add GESTURE_X_OFFSET and GESTURE_Y_OFFSET.
+// If you added a new axis, consider whether this should also be exposed as a HAL axis. Update the
+// static_assert below and add the new axis here, or leave a comment summarizing your decision.
+static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) ==
+ static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET));
static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
common::VideoFrame out;
@@ -299,8 +304,8 @@
common::PointerCoords coords;
// OK to copy bits because we have static_assert for pointerCoords axes
coords.bits = args.pointerCoords[i].bits;
- coords.values = std::vector<float>(args.pointerCoords[i].values,
- args.pointerCoords[i].values +
+ coords.values = std::vector<float>(args.pointerCoords[i].values.cbegin(),
+ args.pointerCoords[i].values.cbegin() +
BitSet64::count(args.pointerCoords[i].bits));
outPointerCoords.push_back(coords);
}
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 0972e22..d33b298 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -31,6 +31,11 @@
namespace android {
+std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume) {
+ keep.splice(keep.end(), consume);
+ return keep;
+}
+
// --- InputListenerInterface ---
// Helper to std::visit with lambdas.
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
index e2e64f9..e74f258 100644
--- a/services/inputflinger/InputThread.cpp
+++ b/services/inputflinger/InputThread.cpp
@@ -54,7 +54,13 @@
}
bool InputThread::isCallingThread() {
+#if defined(__ANDROID__)
return gettid() == mThread->getTid();
+#else
+ // Assume that the caller is doing everything correctly,
+ // since thread information is not available on host
+ return false;
+#endif
}
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index 1f37774..b192ad7 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -225,4 +225,27 @@
int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
: id(id), eventTime(eventTime), request(request) {}
+// Helper to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+// explicit deduction guide (not needed as of C++20)
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
+
+const char* toString(const NotifyArgs& args) {
+ Visitor toStringVisitor{
+ [&](const NotifyConfigurationChangedArgs&) { return "NotifyConfigurationChangedArgs"; },
+ [&](const NotifyKeyArgs&) { return "NotifyKeyArgs"; },
+ [&](const NotifyMotionArgs&) { return "NotifyMotionArgs"; },
+ [&](const NotifySensorArgs&) { return "NotifySensorArgs"; },
+ [&](const NotifySwitchArgs&) { return "NotifySwitchArgs"; },
+ [&](const NotifyDeviceResetArgs&) { return "NotifyDeviceResetArgs"; },
+ [&](const NotifyPointerCaptureChangedArgs&) {
+ return "NotifyPointerCaptureChangedArgs";
+ },
+ [&](const NotifyVibratorStateArgs&) { return "NotifyVibratorStateArgs"; },
+ };
+ return std::visit(toStringVisitor, args);
+}
+
} // namespace android
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index beec2e1..ddd5146 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -25,8 +25,7 @@
for (size_t i = 0; i < args.pointerCount; i++) {
// Make sure we are canceling stylus pointers
const int32_t toolType = args.pointerProperties[i].toolType;
- if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ if (isStylusToolType(toolType)) {
hasStylus = true;
}
if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 4a0f2ec..3d7242e 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -15,6 +15,9 @@
"name": "inputflinger_tests"
},
{
+ "name": "libchrome-gestures_test"
+ },
+ {
"name": "libpalmrejection_test"
},
{
@@ -41,6 +44,7 @@
"include-filter": "android.view.cts.input",
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
+ "include-filter": "android.view.cts.TooltipTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
@@ -128,6 +132,7 @@
{
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
+ "include-filter": "android.view.cts.TooltipTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index ec41025..c170b81 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -99,14 +99,17 @@
}
static int getLinuxToolCode(int toolType) {
- if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
- return BTN_TOOL_PEN;
+ switch (toolType) {
+ case AMOTION_EVENT_TOOL_TYPE_STYLUS:
+ return BTN_TOOL_PEN;
+ case AMOTION_EVENT_TOOL_TYPE_ERASER:
+ return BTN_TOOL_RUBBER;
+ case AMOTION_EVENT_TOOL_TYPE_FINGER:
+ return BTN_TOOL_FINGER;
+ default:
+ ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+ return BTN_TOOL_FINGER;
}
- if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
- return BTN_TOOL_FINGER;
- }
- ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
- return BTN_TOOL_FINGER;
}
static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -195,7 +198,7 @@
static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
std::set<int32_t> stylusPointerIds;
for (uint32_t i = 0; i < args.pointerCount; i++) {
- if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+ if (isStylusToolType(args.pointerProperties[i].toolType)) {
stylusPointerIds.insert(args.pointerProperties[i].id);
}
}
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index e5c19af..4e2a6fb 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -21,7 +21,6 @@
"libbinder",
"libcrypto",
"libcutils",
- "libinput",
"libinputflinger_base",
"libinputreporter",
"liblog",
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index eb79b76..ab5c5ef 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -23,6 +23,7 @@
cc_library_headers {
name: "libinputdispatcher_headers",
+ host_supported: true,
export_include_dirs: [
"include",
],
@@ -33,6 +34,7 @@
srcs: [
"AnrTracker.cpp",
"Connection.cpp",
+ "DragState.cpp",
"Entry.cpp",
"FocusResolver.cpp",
"InjectionState.cpp",
@@ -44,8 +46,8 @@
"LatencyAggregator.cpp",
"LatencyTracker.cpp",
"Monitor.cpp",
+ "TouchedWindow.cpp",
"TouchState.cpp",
- "DragState.cpp",
],
}
@@ -56,20 +58,34 @@
"libbase",
"libcrypto",
"libcutils",
- "libinput",
"libkll",
"liblog",
"libprotobuf-cpp-lite",
"libstatslog",
- "libstatspull",
- "libstatssocket",
- "libgui",
"libutils",
"server_configurable_flags",
],
static_libs: [
"libattestation",
+ "libgui_window_info_static",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libgui",
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libstatspull",
+ "libstatssocket",
+ ],
+ },
+ },
header_libs: [
"libinputdispatcher_headers",
],
@@ -84,8 +100,8 @@
shared_libs: [
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
- "libinputreporter",
"libinputflinger_base",
+ "libinputreporter",
],
export_header_lib_headers: [
"libinputdispatcher_headers",
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
index c3f611e..a18063f 100644
--- a/services/inputflinger/dispatcher/AnrTracker.cpp
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -54,7 +54,7 @@
}
// If empty() is false, return the time at which the next connection should cause an ANR
-// If empty() is true, return LONG_LONG_MAX
+// If empty() is true, return LLONG_MAX
nsecs_t AnrTracker::firstTimeout() const {
if (mAnrTimeouts.empty()) {
return std::numeric_limits<nsecs_t>::max();
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
index 5e5e0c4..cff5d00 100644
--- a/services/inputflinger/dispatcher/AnrTracker.h
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -35,7 +35,7 @@
bool empty() const;
// If empty() is false, return the time at which the next connection should cause an ANR
- // If empty() is true, return LONG_LONG_MAX
+ // If empty() is true, return LLONG_MAX
nsecs_t firstTimeout() const;
// Return the token of the next connection that should cause an ANR.
// Do not call this unless empty() is false, you will encounter undefined behaviour.
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index d210e9e..512cb6e 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -24,7 +24,7 @@
/* Specifies which events are to be canceled and why. */
struct CancelationOptions {
- enum Mode {
+ enum class Mode {
CANCEL_ALL_EVENTS = 0,
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 33e7e17..7bbfb95 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -166,7 +166,7 @@
repeatCount(repeatCount),
downTime(downTime),
syntheticRepeat(false),
- interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
+ interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
interceptKeyWakeupTime(0) {}
KeyEntry::~KeyEntry() {}
@@ -189,7 +189,7 @@
dispatchInProgress = false;
syntheticRepeat = false;
- interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
interceptKeyWakeupTime = 0;
}
@@ -308,7 +308,8 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
+ ftl::Flags<InputTarget::Flags> targetFlags,
const ui::Transform& transform, const ui::Transform& rawTransform,
float globalScaleFactor)
: seq(nextSeq()),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 60f319a..3799814 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -140,11 +140,11 @@
bool syntheticRepeat; // set to true for synthetic key repeats
- enum InterceptKeyResult {
- INTERCEPT_KEY_RESULT_UNKNOWN,
- INTERCEPT_KEY_RESULT_SKIP,
- INTERCEPT_KEY_RESULT_CONTINUE,
- INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
+ enum class InterceptKeyResult {
+ UNKNOWN,
+ SKIP,
+ CONTINUE,
+ TRY_AGAIN_LATER,
};
InterceptKeyResult interceptKeyResult; // set based on the interception result
nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
@@ -223,7 +223,7 @@
const uint32_t seq; // unique sequence number, never 0
std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
- int32_t targetFlags;
+ ftl::Flags<InputTarget::Flags> targetFlags;
ui::Transform transform;
ui::Transform rawTransform;
float globalScaleFactor;
@@ -238,13 +238,15 @@
int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- const ui::Transform& transform, const ui::Transform& rawTransform,
- float globalScaleFactor);
+ DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
+ ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform,
+ const ui::Transform& rawTransform, float globalScaleFactor);
- inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
+ inline bool hasForegroundTarget() const {
+ return targetFlags.test(InputTarget::Flags::FOREGROUND);
+ }
- inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; }
+ inline bool isSplit() const { return targetFlags.test(InputTarget::Flags::SPLIT); }
private:
static volatile int32_t sNextSeqAtomic;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 399153c..4aac377 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -25,8 +25,11 @@
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
#include <ftl/enum.h>
+#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
+#endif
#include <input/InputDevice.h>
+#include <input/PrintTools.h>
#include <powermanager/PowerManager.h>
#include <unistd.h>
#include <utils/Trace.h>
@@ -48,6 +51,7 @@
#define INDENT3 " "
#define INDENT4 " "
+using namespace android::ftl::flag_operators;
using android::base::HwTimeoutMultiplier;
using android::base::Result;
using android::base::StringPrintf;
@@ -56,7 +60,6 @@
using android::gui::TouchOcclusionMode;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
-using android::os::IInputConstants;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
@@ -145,21 +148,23 @@
}
bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
+ switch (MotionEvent::getActionMasked(action)) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
+ return pointerCount == 1;
case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return pointerCount >= 1;
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_SCROLL:
return true;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
- int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && index < pointerCount;
+ const int32_t index = MotionEvent::getActionIndex(action);
+ return index >= 0 && index < pointerCount && pointerCount > 1;
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
@@ -239,9 +244,9 @@
}
dump.append(INDENT4);
dump += entry.eventEntry->getDescription();
- dump += StringPrintf(", seq=%" PRIu32
- ", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 "ms",
- entry.seq, entry.targetFlags, entry.resolvedAction,
+ dump += StringPrintf(", seq=%" PRIu32 ", targetFlags=%s, resolvedAction=%d, age=%" PRId64
+ "ms",
+ entry.seq, entry.targetFlags.string().c_str(), entry.resolvedAction,
ns2ms(currentTime - entry.eventEntry->eventTime));
if (entry.deliveryTime != 0) {
// This entry was delivered, so add information on how long we've been waiting
@@ -286,9 +291,9 @@
first->applicationInfo.token == second->applicationInfo.token;
}
-std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
- std::shared_ptr<EventEntry> eventEntry,
- int32_t inputTargetFlags) {
+std::unique_ptr<DispatchEntry> createDispatchEntry(
+ const InputTarget& inputTarget, std::shared_ptr<EventEntry> eventEntry,
+ ftl::Flags<InputTarget::Flags> inputTargetFlags) {
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
@@ -477,15 +482,14 @@
bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
- (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER);
+ isStylusToolType(entry.pointerProperties[pointerIndex].toolType);
}
-// Determines if the given window can be targeted as InputTarget::FLAG_FOREGROUND.
+// Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND.
// Foreground events are only sent to "foreground targetable" windows, but not all gestures sent to
// such window are necessarily targeted with the flag. For example, an event with ACTION_OUTSIDE can
// be sent to such a window, but it is not a foreground event and doesn't use
-// InputTarget::FLAG_FOREGROUND.
+// InputTarget::Flags::FOREGROUND.
bool canReceiveForegroundTouches(const WindowInfo& info) {
// A non-touchable window can still receive touch events (e.g. in the case of
// STYLUS_INTERCEPTOR), so prevent such windows from receiving foreground events for touches.
@@ -524,6 +528,32 @@
return {};
}
+Point resolveTouchedPosition(const MotionEntry& entry) {
+ const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+ // Always dispatch mouse events to cursor position.
+ if (isFromMouse) {
+ return Point(static_cast<int32_t>(entry.xCursorPosition),
+ static_cast<int32_t>(entry.yCursorPosition));
+ }
+
+ const int32_t pointerIndex = getMotionEventActionPointerIndex(entry.action);
+ return Point(static_cast<int32_t>(
+ entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)),
+ static_cast<int32_t>(
+ entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)));
+}
+
+std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
+ if (eventEntry.type == EventEntry::Type::KEY) {
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ return keyEntry.downTime;
+ } else if (eventEntry.type == EventEntry::Type::MOTION) {
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+ return motionEntry.downTime;
+ }
+ return std::nullopt;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -538,7 +568,7 @@
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
mAppSwitchSawKeyDown(false),
- mAppSwitchDueTime(LONG_LONG_MAX),
+ mAppSwitchDueTime(LLONG_MAX),
mNextUnblockedEvent(nullptr),
mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
@@ -554,8 +584,9 @@
mReporter = createInputReporter();
mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
+#if defined(__ANDROID__)
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
-
+#endif
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
}
@@ -594,7 +625,7 @@
}
void InputDispatcher::dispatchOnce() {
- nsecs_t nextWakeupTime = LONG_LONG_MAX;
+ nsecs_t nextWakeupTime = LLONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
@@ -608,7 +639,7 @@
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptable()) {
- nextWakeupTime = LONG_LONG_MIN;
+ nextWakeupTime = LLONG_MIN;
}
// If we are still waiting for ack on some events,
@@ -618,7 +649,7 @@
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
- if (nextWakeupTime == LONG_LONG_MAX) {
+ if (nextWakeupTime == LLONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
@@ -663,14 +694,14 @@
*/
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
- nsecs_t nextAnrCheck = LONG_LONG_MAX;
+ nsecs_t nextAnrCheck = LLONG_MAX;
// Check if we are waiting for a focused window to appear. Raise ANR if waited too long
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.reset();
mNoFocusedWindowTimeoutTime = std::nullopt;
- return LONG_LONG_MIN;
+ return LLONG_MIN;
} else {
// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
@@ -693,7 +724,7 @@
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
onAnrLocked(connection);
- return LONG_LONG_MIN;
+ return LLONG_MIN;
}
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
@@ -900,7 +931,7 @@
mLastDropReason = dropReason;
releasePendingEventLocked();
- *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
}
}
@@ -921,13 +952,10 @@
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
- int32_t displayId = motionEntry.displayId;
- int32_t x = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = static_cast<int32_t>(
- motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-
+ const int32_t displayId = motionEntry.displayId;
+ const auto [x, y] = resolveTouchedPosition(motionEntry);
const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
+
sp<WindowInfoHandle> touchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus);
if (touchedWindowHandle != nullptr &&
@@ -1006,8 +1034,8 @@
KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
if (pendingKey.keyCode == keyEntry.keyCode &&
pendingKey.interceptKeyResult ==
- KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
- pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
+ pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
pendingKey.interceptKeyWakeupTime = 0;
needWake = true;
}
@@ -1056,7 +1084,7 @@
int32_t y, TouchState* touchState,
bool isStylus,
bool addOutsideTargets,
- bool ignoreDragWindow) {
+ bool ignoreDragWindow) const {
if (addOutsideTargets && touchState == nullptr) {
LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
}
@@ -1074,7 +1102,7 @@
if (addOutsideTargets &&
info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
- touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+ touchState->addOrUpdateWindow(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
BitSet32(0));
}
}
@@ -1142,17 +1170,18 @@
switch (entry.type) {
case EventEntry::Type::KEY: {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+ reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
break;
@@ -1186,11 +1215,11 @@
}
bool InputDispatcher::isAppSwitchPendingLocked() {
- return mAppSwitchDueTime != LONG_LONG_MAX;
+ return mAppSwitchDueTime != LLONG_MAX;
}
void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
- mAppSwitchDueTime = LONG_LONG_MAX;
+ mAppSwitchDueTime = LLONG_MAX;
if (DEBUG_APP_SWITCH) {
if (handled) {
@@ -1307,7 +1336,7 @@
resetKeyRepeatLocked();
}
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset");
options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
return true;
@@ -1345,7 +1374,7 @@
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
channel->getName();
@@ -1419,7 +1448,7 @@
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
dispatchEventLocked(currentTime, entry, {target});
@@ -1456,7 +1485,7 @@
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = InputTarget::Flags::DISPATCH_AS_IS;
inputTargets.push_back(target);
}
return inputTargets;
@@ -1483,7 +1512,7 @@
// stop the key repeat on current device.
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
- mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+ mKeyRepeatState.nextRepeatTime = LLONG_MAX; // don't generate repeats ourselves
} else {
// Not a repeat. Save key down state in case we do see a repeat later.
resetKeyRepeatLocked();
@@ -1512,19 +1541,19 @@
}
// Handle case where the policy asked us to try again later last time.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
+ if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
if (currentTime < entry->interceptKeyWakeupTime) {
if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
*nextWakeupTime = entry->interceptKeyWakeupTime;
}
return false; // wait until next wakeup
}
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
entry->interceptKeyWakeupTime = 0;
}
// Give the policy a chance to intercept the key.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
sp<IBinder> focusedWindowToken =
mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
@@ -1535,9 +1564,9 @@
postCommandLocked(std::move(command));
return false; // wait for the command to run
} else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
}
- } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+ } else if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::SKIP) {
if (*dropReason == DropReason::NOT_DROPPED) {
*dropReason = DropReason::POLICY;
}
@@ -1553,9 +1582,10 @@
}
// Identify targets.
- std::vector<InputTarget> inputTargets;
- InputEventInjectionResult injectionResult =
- findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
+ InputEventInjectionResult injectionResult;
+ sp<WindowInfoHandle> focusedWindow =
+ findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime,
+ /*byref*/ injectionResult);
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
@@ -1564,6 +1594,12 @@
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
return true;
}
+ LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);
+
+ std::vector<InputTarget> inputTargets;
+ addWindowTargetLocked(focusedWindow,
+ InputTarget::Flags::FOREGROUND | InputTarget::Flags::DISPATCH_AS_IS,
+ BitSet32(0), getDownTime(*entry), inputTargets);
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
@@ -1658,13 +1694,27 @@
pilferPointersLocked(mDragState->dragWindow->getToken());
}
- injectionResult =
- findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
- &conflictingPointerActions);
+ std::vector<TouchedWindow> touchedWindows =
+ findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
+ /*byref*/ injectionResult);
+ for (const TouchedWindow& touchedWindow : touchedWindows) {
+ LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED,
+ "Shouldn't be adding window if the injection didn't succeed.");
+ addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+ inputTargets);
+ }
} else {
// Non touch event. (eg. trackball)
- injectionResult =
- findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
+ sp<WindowInfoHandle> focusedWindow =
+ 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,
+ BitSet32(0), getDownTime(*entry), inputTargets);
+ }
}
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
@@ -1675,9 +1725,9 @@
return true;
}
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
- CancelationOptions::Mode mode(isPointerEvent
- ? CancelationOptions::CANCEL_POINTER_EVENTS
- : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+ CancelationOptions::Mode mode(
+ isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
+ : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
CancelationOptions options(mode, "input event injection failed");
synthesizeCancelationEventsForMonitorsLocked(options);
return true;
@@ -1688,7 +1738,7 @@
// Dispatch the motion.
if (conflictingPointerActions) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"conflicting pointer actions");
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
@@ -1714,22 +1764,23 @@
}
InputTarget target;
target.inputChannel = channel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
dispatchEventLocked(currentTime, entry, {target});
}
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32
", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
- entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
- entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
- entry.xPrecision, entry.yPrecision, entry.downTime);
+ prefix, entry.eventTime, entry.deviceId,
+ inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags,
+ MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags,
+ entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
+ entry.yPrecision, entry.downTime);
for (uint32_t i = 0; i < entry.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
@@ -1788,7 +1839,7 @@
ALOGW("Canceling events for %s because it is unresponsive",
connection->inputChannel->getName().c_str());
if (connection->status == Connection::Status::NORMAL) {
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS,
"application not responding");
synthesizeCancelationEventsForConnectionLocked(connection, options);
}
@@ -1866,21 +1917,11 @@
return false;
}
-static std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) {
- if (eventEntry.type == EventEntry::Type::KEY) {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
- return keyEntry.downTime;
- } else if (eventEntry.type == EventEntry::Type::MOTION) {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
- return motionEntry.downTime;
- }
- return std::nullopt;
-}
-
-InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
- nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) {
+sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(
+ nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+ InputEventInjectionResult& outInjectionResult) {
std::string reason;
+ outInjectionResult = InputEventInjectionResult::FAILED; // Default result
int32_t displayId = getTargetDisplayId(entry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
@@ -1893,12 +1934,12 @@
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
ftl::enum_string(entry.type).c_str(), displayId);
- return InputEventInjectionResult::FAILED;
+ return nullptr;
}
// Drop key events if requested by input feature
if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
- return InputEventInjectionResult::FAILED;
+ return nullptr;
}
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -1918,15 +1959,17 @@
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
ftl::enum_string(entry.type).c_str());
- return InputEventInjectionResult::FAILED;
+ return nullptr;
} else {
// Still waiting for the focused window
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
}
}
@@ -1936,13 +1979,15 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
- return InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+ return nullptr;
}
if (focusedWindowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
}
// If the event is a key event, then we must wait for all previous events to
@@ -1959,17 +2004,13 @@
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
- return InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
+ return nullptr;
}
}
- // Success! Output targets.
- addWindowTargetLocked(focusedWindowHandle,
- InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0), getDownTime(entry), inputTargets);
-
- // Done.
- return InputEventInjectionResult::SUCCEEDED;
+ outInjectionResult = InputEventInjectionResult::SUCCEEDED;
+ return focusedWindowHandle;
}
/**
@@ -1998,11 +2039,42 @@
return responsiveMonitors;
}
-InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
+/**
+ * In general, touch should be always split between windows. Some exceptions:
+ * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
+ * from the same device, *and* the window that's receiving the current pointer does not support
+ * split touch.
+ * 2. Don't split mouse events
+ */
+bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
+ const MotionEntry& entry) const {
+ if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
+ // We should never split mouse events
+ return false;
+ }
+ for (const TouchedWindow& touchedWindow : touchState.windows) {
+ if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+ // Spy windows should not affect whether or not touch is split.
+ continue;
+ }
+ if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
+ continue;
+ }
+ // Eventually, touchedWindow will contain the deviceId of each pointer that's currently
+ // being sent there. For now, use deviceId from touch state.
+ if (entry.deviceId == touchState.deviceId && !touchedWindow.pointerIds.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+ InputEventInjectionResult& outInjectionResult) {
ATRACE_CALL();
+ std::vector<TouchedWindow> touchedWindows;
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
const int32_t displayId = entry.displayId;
@@ -2010,7 +2082,7 @@
const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
- InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
+ outInjectionResult = InputEventInjectionResult::PENDING;
sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
sp<WindowInfoHandle> newTouchedWindowHandle;
@@ -2024,10 +2096,9 @@
tempTouchState = *oldState;
}
- bool isSplit = tempTouchState.split;
- bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
- (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
- tempTouchState.displayId != displayId);
+ bool isSplit = shouldSplitTouch(tempTouchState, entry);
+ const bool switchedDevice = (oldState != nullptr) &&
+ (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2035,50 +2106,34 @@
const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
- bool wrongDevice = false;
+
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
+ if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) {
ALOGI("Dropping event because a pointer for a different device is already down "
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = InputEventInjectionResult::FAILED;
- switchedDevice = false;
- wrongDevice = true;
- goto Failed;
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return touchedWindows; // wrong device
}
tempTouchState.reset();
- tempTouchState.down = down;
tempTouchState.deviceId = entry.deviceId;
tempTouchState.source = entry.source;
- tempTouchState.displayId = displayId;
isSplit = false;
} else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
ALOGI("Dropping move event because a pointer for a different device is already active "
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = InputEventInjectionResult::FAILED;
- switchedDevice = false;
- wrongDevice = true;
- goto Failed;
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return touchedWindows; // wrong device
}
if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
-
- int32_t x;
- int32_t y;
+ const auto [x, y] = resolveTouchedPosition(entry);
const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- // Always dispatch mouse events to cursor position.
- if (isFromMouse) {
- x = int32_t(entry.xCursorPosition);
- y = int32_t(entry.yCursorPosition);
- } else {
- x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
- y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
- }
const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
@@ -2095,7 +2150,7 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected touch event: %s", (*err).c_str());
- injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
newTouchedWindowHandle = nullptr;
goto Failed;
}
@@ -2114,7 +2169,7 @@
// No window is touched, so set split to true. This will allow the next pointer down to
// be delivered to a new window which supports split touch. Pointers from a mouse device
// should never be split.
- tempTouchState.split = isSplit = !isFromMouse;
+ isSplit = !isFromMouse;
}
// Update hover state.
@@ -2136,66 +2191,30 @@
if (newTouchedWindows.empty()) {
ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
x, y, displayId);
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
- const WindowInfo& info = *windowHandle->getInfo();
-
- // Skip spy window targets that are not valid for targeted injection.
- if (const auto err = verifyTargetedInjection(windowHandle, entry); err) {
- continue;
- }
-
- if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
- ALOGI("Not sending touch event to %s because it is paused",
- windowHandle->getName().c_str());
- continue;
- }
-
- // Ensure the window has a connection and the connection is responsive
- const bool isResponsive = hasResponsiveConnectionLocked(*windowHandle);
- if (!isResponsive) {
- ALOGW("Not sending touch gesture to %s because it is not responsive",
- windowHandle->getName().c_str());
- continue;
- }
-
- // Drop events that can't be trusted due to occlusion
- TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(windowHandle, x, y);
- if (!isTouchTrustedLocked(occlusionInfo)) {
- if (DEBUG_TOUCH_OCCLUSION) {
- ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
- for (const auto& log : occlusionInfo.debugInfo) {
- ALOGD("%s", log.c_str());
- }
- }
- ALOGW("Dropping untrusted touch event due to %s/%d",
- occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
- continue;
- }
-
- // Drop touch events if requested by input feature
- if (shouldDropInput(entry, windowHandle)) {
+ if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
continue;
}
// Set target flags.
- int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS;
+ ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS;
if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
// There should only be one touched window that can be "foreground" for the pointer.
- targetFlags |= InputTarget::FLAG_FOREGROUND;
+ targetFlags |= InputTarget::Flags::FOREGROUND;
}
if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
+ targetFlags |= InputTarget::Flags::SPLIT;
}
if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
} else if (isWindowObscuredLocked(windowHandle)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
// Update the temporary touch state.
@@ -2218,13 +2237,12 @@
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!tempTouchState.down) {
- if (DEBUG_FOCUS) {
- ALOGD("Dropping event because the pointer is not down or we previously "
- "dropped the pointer down event in display %" PRId32,
- displayId);
- }
- injectionResult = InputEventInjectionResult::FAILED;
+ if (!tempTouchState.isDown()) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "Dropping event because the pointer is not down or we previously "
+ "dropped the pointer down event in display %" PRId32 ": %s",
+ displayId, entry.getDescription().c_str());
+ outInjectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -2233,9 +2251,7 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
- const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-
+ const auto [x, y] = resolveTouchedPosition(entry);
const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
@@ -2245,7 +2261,7 @@
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
- injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
newTouchedWindowHandle = nullptr;
goto Failed;
}
@@ -2265,7 +2281,7 @@
}
// Make a slippery exit from the old window.
tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
BitSet32(0));
// Make a slippery entrance into the new window.
@@ -2273,17 +2289,18 @@
isSplit = !isFromMouse;
}
- int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
+ ftl::Flags<InputTarget::Flags> targetFlags =
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER;
if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
- targetFlags |= InputTarget::FLAG_FOREGROUND;
+ targetFlags |= InputTarget::Flags::FOREGROUND;
}
if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
+ targetFlags |= InputTarget::Flags::SPLIT;
}
if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
} else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
BitSet32 pointerIds;
@@ -2292,6 +2309,20 @@
entry.eventTime);
}
}
+
+ // Update the pointerIds for non-splittable when it received pointer down.
+ if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // If no split, we suppose all touched windows should receive pointer down.
+ const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ // Ignore drag window for it should just track one pointer.
+ if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+ continue;
+ }
+ touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
+ }
+ }
}
// Update dispatching for hover enter and exit.
@@ -2306,7 +2337,8 @@
mLastHoverWindowHandle->getName().c_str());
}
tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
+ InputTarget::Flags::DISPATCH_AS_HOVER_EXIT,
+ BitSet32(0));
}
// Let the new window know that the hover sequence is starting, unless we already did it
@@ -2319,7 +2351,7 @@
newHoverWindowHandle->getName().c_str());
}
tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+ InputTarget::Flags::DISPATCH_AS_HOVER_ENTER,
BitSet32(0));
}
}
@@ -2332,11 +2364,11 @@
[](const TouchedWindow& touchedWindow) {
return !canReceiveForegroundTouches(
*touchedWindow.windowHandle->getInfo()) ||
- (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
+ touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND);
})) {
ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
displayId, entry.getDescription().c_str());
- injectionResult = InputEventInjectionResult::FAILED;
+ outInjectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -2344,7 +2376,7 @@
if (entry.injectionState != nullptr) {
std::string errs;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
// Allow ACTION_OUTSIDE events generated by targeted injection to be
// dispatched to any uid, since the coords will be zeroed out later.
continue;
@@ -2356,7 +2388,7 @@
ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
"%d:%s",
*entry.injectionState->targetUid, errs.c_str());
- injectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+ outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
goto Failed;
}
}
@@ -2369,11 +2401,11 @@
if (foregroundWindowHandle) {
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle;
if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) {
tempTouchState.addOrUpdateWindow(windowInfoHandle,
- InputTarget::FLAG_ZERO_COORDS,
+ InputTarget::Flags::ZERO_COORDS,
BitSet32(0));
}
}
@@ -2400,26 +2432,22 @@
if (info->displayId == displayId &&
windowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::IS_WALLPAPER)) {
- tempTouchState
- .addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_WINDOW_IS_OBSCURED |
- InputTarget::
- FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
- InputTarget::FLAG_DISPATCH_AS_IS,
- BitSet32(0), entry.eventTime);
+ BitSet32 pointerIds;
+ pointerIds.markBit(entry.pointerProperties[0].id);
+ tempTouchState.addOrUpdateWindow(windowHandle,
+ InputTarget::Flags::WINDOW_IS_OBSCURED |
+ InputTarget::Flags::
+ WINDOW_IS_PARTIALLY_OBSCURED |
+ InputTarget::Flags::DISPATCH_AS_IS,
+ pointerIds, entry.eventTime);
}
}
}
}
// Success! Output targets.
- injectionResult = InputEventInjectionResult::SUCCEEDED;
-
- for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
- inputTargets);
- }
+ touchedWindows = tempTouchState.windows;
+ outInjectionResult = InputEventInjectionResult::SUCCEEDED;
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
@@ -2427,84 +2455,70 @@
Failed:
// Update final pieces of touch state if the injector had permission.
- if (!wrongDevice) {
- if (switchedDevice) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Switched to a different device.");
- }
- *outConflictingPointerActions = true;
+ if (switchedDevice) {
+ if (DEBUG_FOCUS) {
+ ALOGD("Conflicting pointer actions: Switched to a different device.");
}
-
- if (isHoverAction) {
- // Started hovering, therefore no longer down.
- if (oldState && oldState->down) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Hover received while pointer was "
- "down.");
- }
- *outConflictingPointerActions = true;
- }
- tempTouchState.reset();
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- tempTouchState.deviceId = entry.deviceId;
- tempTouchState.source = entry.source;
- tempTouchState.displayId = displayId;
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
- maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
- // All pointers up or canceled.
- tempTouchState.reset();
- } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
- // First pointer went down.
- if (oldState && oldState->down) {
- if (DEBUG_FOCUS) {
- ALOGD("Conflicting pointer actions: Down received while already down.");
- }
- *outConflictingPointerActions = true;
- }
- } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- // One pointer went up.
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
- for (size_t i = 0; i < tempTouchState.windows.size();) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- touchedWindow.pointerIds.clearBit(pointerId);
- if (touchedWindow.pointerIds.isEmpty()) {
- tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
- continue;
- }
- i += 1;
- }
- } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
- // If no split, we suppose all touched windows should receive pointer down.
- const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- // Ignore drag window for it should just track one pointer.
- if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
- continue;
- }
- touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
- }
- }
-
- // Save changes unless the action was scroll in which case the temporary touch
- // state was only valid for this one action.
- if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
- if (tempTouchState.displayId >= 0) {
- mTouchStatesByDisplay[displayId] = tempTouchState;
- } else {
- mTouchStatesByDisplay.erase(displayId);
- }
- }
-
- // Update hover state.
- mLastHoverWindowHandle = newHoverWindowHandle;
+ *outConflictingPointerActions = true;
}
- return injectionResult;
+ if (isHoverAction) {
+ // Started hovering, therefore no longer down.
+ if (oldState && oldState->isDown()) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "Conflicting pointer actions: Hover received while pointer was down.");
+ *outConflictingPointerActions = true;
+ }
+ tempTouchState.reset();
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ tempTouchState.deviceId = entry.deviceId;
+ tempTouchState.source = entry.source;
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+ maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // All pointers up or canceled.
+ tempTouchState.reset();
+ } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ // First pointer went down.
+ if (oldState && oldState->isDown()) {
+ ALOGD("Conflicting pointer actions: Down received while already down.");
+ *outConflictingPointerActions = true;
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ // One pointer went up.
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
+
+ for (size_t i = 0; i < tempTouchState.windows.size();) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ touchedWindow.pointerIds.clearBit(pointerId);
+ if (touchedWindow.pointerIds.isEmpty()) {
+ tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+ continue;
+ }
+ i += 1;
+ }
+ }
+
+ // Save changes unless the action was scroll in which case the temporary touch
+ // state was only valid for this one action.
+ if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
+ if (displayId >= 0) {
+ mTouchStatesByDisplay[displayId] = tempTouchState;
+ } else {
+ mTouchStatesByDisplay.erase(displayId);
+ }
+ }
+
+ if (tempTouchState.windows.empty()) {
+ mTouchStatesByDisplay.erase(displayId);
+ }
+
+ // Update hover state.
+ mLastHoverWindowHandle = newHoverWindowHandle;
+
+ return touchedWindows;
}
void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
@@ -2609,9 +2623,10 @@
}
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ BitSet32 pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
- std::vector<InputTarget>& inputTargets) {
+ std::vector<InputTarget>& inputTargets) const {
std::vector<InputTarget>::iterator it =
std::find_if(inputTargets.begin(), inputTargets.end(),
[&windowHandle](const InputTarget& inputTarget) {
@@ -2657,7 +2672,7 @@
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
InputTarget target;
target.inputChannel = monitor.inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ 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()) {
@@ -2689,7 +2704,7 @@
// We do want to potentially flag touchable windows even if they have 0
// opacity, since they can consume touches and alter the effects of the
// user interaction (eg. apps that rely on
- // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those
+ // Flags::WINDOW_IS_PARTIALLY_OBSCURED should still be told about those
// windows), hence we also check for FLAG_NOT_TOUCHABLE.
return false;
} else if (info->ownerUid == otherInfo->ownerUid) {
@@ -2918,9 +2933,9 @@
ATRACE_NAME(message.c_str());
}
if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+ ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
"globalScaleFactor=%f, pointerIds=0x%x %s",
- connection->getInputChannelName().c_str(), inputTarget.flags,
+ connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(),
inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
inputTarget.getPointerInfoString().c_str());
}
@@ -2937,9 +2952,9 @@
}
// Split a motion event if needed.
- if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
+ if (inputTarget.flags.test(InputTarget::Flags::SPLIT)) {
LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
- "Entry type %s should not have FLAG_SPLIT",
+ "Entry type %s should not have Flags::SPLIT",
ftl::enum_string(eventEntry->type).c_str());
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
@@ -2983,22 +2998,24 @@
connection->getInputChannelName().c_str(), eventEntry->id);
ATRACE_NAME(message.c_str());
}
+ 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::FLAG_DISPATCH_AS_HOVER_EXIT);
+ InputTarget::Flags::DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
+ InputTarget::Flags::DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
+ InputTarget::Flags::DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_IS);
+ InputTarget::Flags::DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
- InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.empty()) {
@@ -3009,18 +3026,20 @@
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget,
- int32_t dispatchMode) {
+ ftl::Flags<InputTarget::Flags> dispatchMode) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
connection->getInputChannelName().c_str(),
- dispatchModeToString(dispatchMode).c_str());
+ dispatchMode.string().c_str());
ATRACE_NAME(message.c_str());
}
- int32_t inputTargetFlags = inputTarget.flags;
- if (!(inputTargetFlags & dispatchMode)) {
+ ftl::Flags<InputTarget::Flags> inputTargetFlags = inputTarget.flags;
+ if (!inputTargetFlags.any(dispatchMode)) {
return;
}
- inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
+
+ inputTargetFlags.clear(InputTarget::DISPATCH_MASK);
+ inputTargetFlags |= dispatchMode;
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
@@ -3057,15 +3076,15 @@
constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
static_cast<int32_t>(IdGenerator::Source::OTHER);
dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
- if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_EXIT)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_HOVER_ENTER)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
- } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+ } else if (dispatchMode.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_ENTER)) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
} else {
dispatchEntry->resolvedAction = motionEntry.action;
@@ -3085,10 +3104,10 @@
}
dispatchEntry->resolvedFlags = motionEntry.flags;
- if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+ if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_OBSCURED)) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
}
- if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
+ if (dispatchEntry->targetFlags.test(InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED)) {
dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
@@ -3188,8 +3207,7 @@
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
std::vector<sp<Connection>> newConnections;
for (const InputTarget& target : targets) {
- if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) ==
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
continue; // Skip windows that receive ACTION_OUTSIDE
}
@@ -3238,6 +3256,55 @@
postCommandLocked(std::move(command));
}
+status_t InputDispatcher::publishMotionEvent(Connection& connection,
+ DispatchEntry& dispatchEntry) const {
+ const EventEntry& eventEntry = *(dispatchEntry.eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+
+ PointerCoords scaledCoords[MAX_POINTERS];
+ const PointerCoords* usingCoords = motionEntry.pointerCoords;
+
+ // Set the X and Y offset and X and Y scale depending on the input source.
+ if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
+ !(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) {
+ float globalScaleFactor = dispatchEntry.globalScaleFactor;
+ if (globalScaleFactor != 1.0f) {
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ scaledCoords[i] = motionEntry.pointerCoords[i];
+ // Don't apply window scale here since we don't want scale to affect raw
+ // coordinates. The scale will be sent back to the client and applied
+ // later when requesting relative coordinates.
+ scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+ 1 /* windowYScale */);
+ }
+ usingCoords = scaledCoords;
+ }
+ } else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) {
+ // We don't want the dispatch target to know the coordinates
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ scaledCoords[i].clear();
+ }
+ usingCoords = scaledCoords;
+ }
+
+ std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry);
+
+ // 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.pointerCount, motionEntry.pointerProperties,
+ usingCoords);
+}
+
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
if (ATRACE_ENABLED()) {
@@ -3277,58 +3344,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
-
- PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry.pointerCoords;
-
- // Set the X and Y offset and X and Y scale depending on the input source.
- if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
- !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- float globalScaleFactor = dispatchEntry->globalScaleFactor;
- if (globalScaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
- scaledCoords[i] = motionEntry.pointerCoords[i];
- // Don't apply window scale here since we don't want scale to affect raw
- // coordinates. The scale will be sent back to the client and applied
- // later when requesting relative coordinates.
- scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
- 1 /* windowYScale */);
- }
- usingCoords = scaledCoords;
- }
- } else {
- // We don't want the dispatch target to know.
- if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
- for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
- scaledCoords[i].clear();
- }
- usingCoords = scaledCoords;
- }
- }
-
- std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);
-
- // Publish the motion event.
- status = 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.pointerCount,
- motionEntry.pointerProperties, usingCoords);
+ status = publishMotionEvent(*connection, *dispatchEntry);
break;
}
@@ -3665,7 +3681,7 @@
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = InputTarget::Flags::DISPATCH_AS_IS;
const bool wasEmpty = connection->outboundQueue.empty();
@@ -3700,7 +3716,7 @@
}
enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
- InputTarget::FLAG_DISPATCH_AS_IS);
+ InputTarget::Flags::DISPATCH_AS_IS);
}
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -3736,7 +3752,7 @@
target.globalScaleFactor = windowInfo->globalScaleFactor;
}
target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.flags = InputTarget::Flags::DISPATCH_AS_IS;
const bool wasEmpty = connection->outboundQueue.empty();
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
@@ -3762,7 +3778,7 @@
}
enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
- InputTarget::FLAG_DISPATCH_AS_IS);
+ InputTarget::Flags::DISPATCH_AS_IS);
}
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -4017,13 +4033,14 @@
if (DEBUG_INBOUND_EVENT_DETAILS) {
ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
"displayId=%" PRId32 ", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+ "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
"yCursorPosition=%f, downTime=%" PRId64,
args->id, args->eventTime, args->deviceId, args->source, args->displayId,
- args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
- args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
- args->xCursorPosition, args->yCursorPosition, args->downTime);
+ args->policyFlags, MotionEvent::actionToString(args->action).c_str(),
+ args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags,
+ args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition,
+ args->downTime);
for (uint32_t i = 0; i < args->pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
"x=%f, y=%f, pressure=%f, size=%f, "
@@ -4041,10 +4058,9 @@
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
- if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
- args->pointerProperties)) {
- return;
- }
+ LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties),
+ "Invalid event: %s", args->dump().c_str());
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4607,26 +4623,58 @@
return getWindowHandleLocked(focusedToken, displayId);
}
-bool InputDispatcher::hasResponsiveConnectionLocked(WindowInfoHandle& windowHandle) const {
- sp<Connection> connection = getConnectionLocked(windowHandle.getToken());
- const bool noInputChannel =
- windowHandle.getInfo()->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
- if (connection != nullptr && noInputChannel) {
- ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s",
- windowHandle.getName().c_str(), connection->inputChannel->getName().c_str());
+bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
+ const MotionEntry& motionEntry) const {
+ const WindowInfo& info = *window->getInfo();
+
+ // Skip spy window targets that are not valid for targeted injection.
+ if (const auto err = verifyTargetedInjection(window, motionEntry); err) {
return false;
}
+ if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
+ ALOGI("Not sending touch event to %s because it is paused", window->getName().c_str());
+ return false;
+ }
+
+ if (info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
+ ALOGW("Not sending touch gesture to %s because it has config NO_INPUT_CHANNEL",
+ window->getName().c_str());
+ return false;
+ }
+
+ sp<Connection> connection = getConnectionLocked(window->getToken());
if (connection == nullptr) {
- if (!noInputChannel) {
- ALOGI("Could not find connection for %s", windowHandle.getName().c_str());
- }
+ ALOGW("Not sending touch to %s because there's no corresponding connection",
+ window->getName().c_str());
return false;
}
+
if (!connection->responsive) {
- ALOGW("Window %s is not responsive", windowHandle.getName().c_str());
+ ALOGW("Not sending touch to %s because it is not responsive", window->getName().c_str());
return false;
}
+
+ // Drop events that can't be trusted due to occlusion
+ const auto [x, y] = resolveTouchedPosition(motionEntry);
+ TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, x, y);
+ if (!isTouchTrustedLocked(occlusionInfo)) {
+ if (DEBUG_TOUCH_OCCLUSION) {
+ ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ for (const auto& log : occlusionInfo.debugInfo) {
+ ALOGD("%s", log.c_str());
+ }
+ }
+ ALOGW("Dropping untrusted touch event due to %s/%d", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid);
+ return false;
+ }
+
+ // Drop touch events if requested by input feature
+ if (shouldDropInput(motionEntry, window)) {
+ return false;
+ }
+
return true;
}
@@ -4761,10 +4809,13 @@
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
- if (mLastHoverWindowHandle &&
- std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
- windowHandles.end()) {
- mLastHoverWindowHandle = nullptr;
+ if (mLastHoverWindowHandle) {
+ const WindowInfo* lastHoverWindowInfo = mLastHoverWindowHandle->getInfo();
+ if (lastHoverWindowInfo->displayId == displayId &&
+ std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
+ windowHandles.end()) {
+ mLastHoverWindowHandle = nullptr;
+ }
}
std::optional<FocusResolver::FocusChanges> changes =
@@ -4787,12 +4838,12 @@
std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
// Since we are about to drop the touch, cancel the events for the wallpaper as
// well.
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
touchedWindow.windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
@@ -4833,7 +4884,7 @@
std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(newWindowHandle->getToken());
if (inputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"touched window's orientation changed");
synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
}
@@ -4914,7 +4965,7 @@
getInputChannelLocked(oldFocusedWindowToken);
if (inputChannel != nullptr) {
CancelationOptions
- options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"The display which contains this window no longer has focus.");
options.displayId = ADISPLAY_ID_NONE;
synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
@@ -4934,10 +4985,6 @@
}
}
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -4968,10 +5015,6 @@
} else {
changed = false;
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
if (changed) {
@@ -5013,8 +5056,6 @@
? "not set"
: std::to_string(mTouchModePerDisplay[displayId]).c_str());
- // TODO(b/198499018): Ensure that WM can guarantee that touch mode is properly set when
- // display is created.
auto touchModeIt = mTouchModePerDisplay.find(displayId);
if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) {
return false;
@@ -5068,16 +5109,16 @@
mMaximumObscuringOpacityForTouch = opacity;
}
-std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
- const sp<IBinder>& token) {
+std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
+InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) {
for (auto& [displayId, state] : mTouchStatesByDisplay) {
for (TouchedWindow& w : state.windows) {
if (w.windowHandle->getToken() == token) {
- return std::make_pair(&state, &w);
+ return std::make_tuple(&state, &w, displayId);
}
}
}
- return std::make_pair(nullptr, nullptr);
+ return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT);
}
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
@@ -5093,13 +5134,12 @@
std::scoped_lock _l(mLock);
// Find the target touch state and touched window by fromToken.
- auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
+ auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken);
if (state == nullptr || touchedWindow == nullptr) {
ALOGD("Focus transfer failed because from window is not being touched.");
return false;
}
- const int32_t displayId = state->displayId;
sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
if (toWindowHandle == nullptr) {
ALOGW("Cannot transfer focus because to window not found.");
@@ -5113,16 +5153,16 @@
}
// Erase old window.
- int32_t oldTargetFlags = touchedWindow->targetFlags;
+ ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
BitSet32 pointerIds = touchedWindow->pointerIds;
state->removeWindowByToken(fromToken);
// Add new window.
nsecs_t downTimeInTarget = now();
- int32_t newTargetFlags =
- oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS);
+ ftl::Flags<InputTarget::Flags> newTargetFlags =
+ oldTargetFlags & (InputTarget::Flags::SPLIT | InputTarget::Flags::DISPATCH_AS_IS);
if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
- newTargetFlags |= InputTarget::FLAG_FOREGROUND;
+ newTargetFlags |= InputTarget::Flags::FOREGROUND;
}
state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds, downTimeInTarget);
@@ -5144,15 +5184,11 @@
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions
- options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection);
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -5176,7 +5212,7 @@
sp<WindowInfoHandle> touchedForegroundWindow;
// If multiple foreground windows are touched, return nullptr
for (const TouchedWindow& window : state.windows) {
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
if (touchedForegroundWindow != nullptr) {
ALOGI("Two or more foreground windows: %s and %s",
touchedForegroundWindow->getName().c_str(),
@@ -5218,7 +5254,7 @@
ALOGD("Resetting and dropping all events (%s).", reason);
}
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
resetKeyRepeatLocked();
@@ -5288,25 +5324,9 @@
if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
- for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
- const TouchState& state = pair.second;
- dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
- state.displayId, toString(state.down), toString(state.split),
- state.deviceId, state.source);
- if (!state.windows.empty()) {
- dump += INDENT3 "Windows:\n";
- for (size_t i = 0; i < state.windows.size(); i++) {
- const TouchedWindow& touchedWindow = state.windows[i];
- dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, "
- "targetFlags=0x%x, firstDownTimeInTarget=%" PRId64
- "ms\n",
- i, touchedWindow.windowHandle->getName().c_str(),
- touchedWindow.pointerIds.value, touchedWindow.targetFlags,
- ns2ms(touchedWindow.firstDownTimeInTarget.value_or(0)));
- }
- } else {
- dump += INDENT3 "Windows: <none>\n";
- }
+ for (const auto& [displayId, state] : mTouchStatesByDisplay) {
+ std::string touchStateDump = addLinePrefix(state.dump(), INDENT2);
+ dump += INDENT2 + std::to_string(displayId) + " : " + touchStateDump;
}
} else {
dump += INDENT "TouchStates: <no displays touched>\n";
@@ -5415,9 +5435,7 @@
if (!mReplacedKeys.empty()) {
dump += INDENT "ReplacedKeys:\n";
- for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
- const KeyReplacement& replacement = pair.first;
- int32_t newKeyCode = pair.second;
+ for (const auto& [replacement, newKeyCode] : mReplacedKeys) {
dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
replacement.keyCode, replacement.deviceId, newKeyCode);
}
@@ -5651,8 +5669,8 @@
return BAD_VALUE;
}
- auto [statePtr, windowPtr] = findTouchStateAndWindowLocked(token);
- if (statePtr == nullptr || windowPtr == nullptr || !statePtr->down) {
+ auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
+ if (statePtr == nullptr || windowPtr == nullptr || windowPtr->pointerIds.isEmpty()) {
ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
" Ignoring.");
return BAD_VALUE;
@@ -5661,14 +5679,11 @@
TouchState& state = *statePtr;
TouchedWindow& window = *windowPtr;
// Send cancel events to all the input channels we're stealing from.
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"input channel stole pointer stream");
options.deviceId = state.deviceId;
- options.displayId = state.displayId;
- if (state.split) {
- // If split pointers then selectively cancel pointers otherwise cancel all pointers
- options.pointerIds = window.pointerIds;
- }
+ options.displayId = displayId;
+ options.pointerIds = window.pointerIds;
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
const std::shared_ptr<InputChannel> channel =
@@ -5687,11 +5702,7 @@
// This only blocks relevant pointers to be sent to other windows
window.isPilferingPointers = true;
- if (state.split) {
- state.cancelPointersForWindowsExcept(window.pointerIds, token);
- } else {
- state.filterWindowsExcept(token);
- }
+ state.cancelPointersForWindowsExcept(window.pointerIds, token);
return OK;
}
@@ -5954,11 +5965,11 @@
} // acquire lock
if (delay < 0) {
- entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+ entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP;
} else if (delay == 0) {
- entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
} else {
- entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+ entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER;
entry.interceptKeyWakeupTime = now() + delay;
}
}
@@ -6068,7 +6079,7 @@
// Cancel the fallback key.
if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"application handled the original non-fallback key "
"or is no longer a foreground target, "
"canceling previously dispatched fallback key");
@@ -6145,7 +6156,7 @@
}
}
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"canceling fallback, policy no longer desires it");
options.keyCode = fallbackKeyCode;
synthesizeCancelationEventsForConnectionLocked(connection, options);
@@ -6298,7 +6309,7 @@
if (changes.oldFocus) {
std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
if (focusedInputChannel) {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
@@ -6438,7 +6449,7 @@
{
std::scoped_lock _l(mLock);
ALOGD("Canceling all ongoing pointer gestures on all displays.");
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"cancel current touch");
synthesizeCancelationEventsForAllConnectionsLocked(options);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 630d21a..5efb39e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -239,7 +239,7 @@
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
- bool addOutsideTargets = false, bool ignoreDragWindow = false) REQUIRES(mLock);
+ bool addOutsideTargets = false, bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
@@ -384,8 +384,8 @@
REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const
REQUIRES(mLock);
- bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
- REQUIRES(mLock);
+ bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window,
+ const MotionEntry& motionEntry) const REQUIRES(mLock);
// Returns all the input targets (with their respective input channels) from the window handles
// passed as argument.
@@ -541,20 +541,21 @@
// shade is pulled down while we are counting down the timeout).
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
+ bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const;
int32_t getTargetDisplayId(const EventEntry& entry);
- android::os::InputEventInjectionResult findFocusedWindowTargetsLocked(
- nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
- nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
+ nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
+ android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
+ std::vector<TouchedWindow> findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
+ android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
- std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
+ std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
@@ -599,8 +600,9 @@
std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
- const InputTarget& inputTarget, int32_t dispatchMode)
- REQUIRES(mLock);
+ const InputTarget& inputTarget,
+ ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock);
+ status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
@@ -674,8 +676,8 @@
bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
- std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
- REQUIRES(mLock);
+ std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
+ findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Statistics gathering.
LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 047c628..563868d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -500,10 +500,10 @@
}
switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+ case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
+ case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
return true;
- case CancelationOptions::CANCEL_FALLBACK_EVENTS:
+ case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
default:
return false;
@@ -521,11 +521,11 @@
}
switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
+ case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
return true;
- case CancelationOptions::CANCEL_POINTER_EVENTS:
+ case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+ case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
default:
return false;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 2df97d9..2f39480 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -24,24 +24,6 @@
namespace android::inputdispatcher {
-std::string dispatchModeToString(int32_t dispatchMode) {
- switch (dispatchMode) {
- case InputTarget::FLAG_DISPATCH_AS_IS:
- return "DISPATCH_AS_IS";
- case InputTarget::FLAG_DISPATCH_AS_OUTSIDE:
- return "DISPATCH_AS_OUTSIDE";
- case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER:
- return "DISPATCH_AS_HOVER_ENTER";
- case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT:
- return "DISPATCH_AS_HOVER_EXIT";
- case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT:
- return "DISPATCH_AS_SLIPPERY_EXIT";
- case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER:
- return "DISPATCH_AS_SLIPPERY_ENTER";
- }
- return StringPrintf("%" PRId32, dispatchMode);
-}
-
void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
// valid pointer property from the input event.
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index b2966f6..61b07fe 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -16,6 +16,7 @@
#pragma once
+#include <ftl/flags.h>
#include <gui/constants.h>
#include <input/InputTransport.h>
#include <ui/Transform.h>
@@ -30,70 +31,70 @@
* window area.
*/
struct InputTarget {
- enum {
+ enum class Flags : uint32_t {
/* This flag indicates that the event is being delivered to a foreground application. */
- FLAG_FOREGROUND = 1 << 0,
+ FOREGROUND = 1 << 0,
/* This flag indicates that the MotionEvent falls within the area of the target
* obscured by another visible window above it. The motion event should be
* delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
- FLAG_WINDOW_IS_OBSCURED = 1 << 1,
+ WINDOW_IS_OBSCURED = 1 << 1,
/* This flag indicates that a motion event is being split across multiple windows. */
- FLAG_SPLIT = 1 << 2,
+ SPLIT = 1 << 2,
/* This flag indicates that the pointer coordinates dispatched to the application
* will be zeroed out to avoid revealing information to an application. This is
* used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
* the same UID from watching all touches. */
- FLAG_ZERO_COORDS = 1 << 3,
+ 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. */
- FLAG_DISPATCH_AS_IS = 1 << 8,
+ 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. */
- FLAG_DISPATCH_AS_OUTSIDE = 1 << 9,
+ 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. */
- FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10,
+ 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. */
- FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11,
+ 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. */
- FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12,
+ 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. */
- FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13,
-
- /* Mask for all dispatch modes. */
- FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE |
- FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT |
- FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
+ 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. */
- FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
-
+ 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;
+
// The input channel to be targeted.
std::shared_ptr<InputChannel> inputChannel;
// Flags for the input target.
- int32_t flags = 0;
+ ftl::Flags<Flags> flags;
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index cf0c38a..c21af9e 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
+#include <android-base/stringprintf.h>
#include <gui/WindowInfo.h>
#include "InputTarget.h"
-
#include "TouchState.h"
+using namespace android::ftl::flag_operators;
+using android::base::StringPrintf;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
@@ -29,18 +31,14 @@
*this = TouchState();
}
-void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags,
- BitSet32 pointerIds, std::optional<nsecs_t> eventTime) {
- if (targetFlags & InputTarget::FLAG_SPLIT) {
- split = true;
- }
-
- for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows[i];
+void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
+ std::optional<nsecs_t> eventTime) {
+ for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
- if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
- touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;
+ 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 an pointer is down for
@@ -73,10 +71,10 @@
void TouchState::filterNonAsIsTouchWindows() {
for (size_t i = 0; i < windows.size();) {
TouchedWindow& window = windows[i];
- if (window.targetFlags &
- (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {
- window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;
- window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;
+ 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);
@@ -105,15 +103,10 @@
std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
}
-void TouchState::filterWindowsExcept(const sp<IBinder>& token) {
- std::erase_if(windows,
- [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; });
-}
-
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
for (size_t i = 0; i < windows.size(); i++) {
const TouchedWindow& window = windows[i];
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
return window.windowHandle;
}
}
@@ -124,7 +117,7 @@
// Must have exactly one foreground window.
bool haveSlipperyForegroundWindow = false;
for (const TouchedWindow& window : windows) {
- if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
if (haveSlipperyForegroundWindow ||
!window.windowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::SLIPPERY)) {
@@ -147,14 +140,25 @@
return nullptr;
}
-sp<WindowInfoHandle> TouchState::getWindow(const sp<IBinder>& token) const {
- for (const TouchedWindow& touchedWindow : windows) {
- const auto& windowHandle = touchedWindow.windowHandle;
- if (windowHandle->getToken() == token) {
- return windowHandle;
+bool TouchState::isDown() const {
+ return std::any_of(windows.begin(), windows.end(),
+ [](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); });
+}
+
+std::string TouchState::dump() const {
+ std::string out;
+ out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
+ inputEventSourceToString(source).c_str());
+ if (!windows.empty()) {
+ out += " Windows:\n";
+ for (size_t i = 0; i < windows.size(); i++) {
+ const TouchedWindow& touchedWindow = windows[i];
+ out += StringPrintf(" %zu : ", i) + touchedWindow.dump();
}
+ } else {
+ out += " Windows: <none>\n";
}
- return nullptr;
+ return out;
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index cf5f1e5..77c1cdf 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -16,7 +16,6 @@
#pragma once
-#include "Monitor.h"
#include "TouchedWindow.h"
namespace android {
@@ -28,15 +27,10 @@
namespace inputdispatcher {
struct TouchState {
- bool down = false;
- bool split = false;
-
// id of the device that is currently down, others are rejected
int32_t deviceId = -1;
// source of the device that is current down, others are rejected
uint32_t source = 0;
- // id to the display that currently has a touch, others are rejected
- int32_t displayId = ADISPLAY_ID_NONE;
std::vector<TouchedWindow> windows;
@@ -46,11 +40,10 @@
void reset();
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t targetFlags, BitSet32 pointerIds,
+ ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> eventTime = std::nullopt);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
- void filterWindowsExcept(const sp<IBinder>& token);
// Cancel pointers for current set of windows except the window with particular binder token.
void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token);
@@ -61,7 +54,9 @@
sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
- sp<android::gui::WindowInfoHandle> getWindow(const sp<IBinder>&) const;
+ // Whether any of the windows are currently being touched
+ bool isDown() const;
+ std::string dump() const;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
new file mode 100644
index 0000000..af74598
--- /dev/null
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "TouchedWindow.h"
+
+#include <android-base/stringprintf.h>
+#include <input/PrintTools.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+namespace inputdispatcher {
+
+std::string TouchedWindow::dump() const {
+ return StringPrintf("name='%s', pointerIds=0x%0x, "
+ "targetFlags=%s, firstDownTimeInTarget=%s\n",
+ windowHandle->getName().c_str(), pointerIds.value,
+ targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str());
+}
+
+} // namespace inputdispatcher
+} // namespace android
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index a6c505b..dd08323 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -16,23 +16,24 @@
#pragma once
-namespace android {
+#include <gui/WindowInfo.h>
+#include <utils/BitSet.h>
+#include "InputTarget.h"
-namespace gui {
-class WindowInfoHandle;
-}
+namespace android {
namespace inputdispatcher {
// Focus tracking for touch.
struct TouchedWindow {
sp<gui::WindowInfoHandle> windowHandle;
- int32_t targetFlags;
+ ftl::Flags<InputTarget::Flags> targetFlags;
BitSet32 pointerIds;
bool isPilferingPointers = false;
// Time at which the first action down occurred on this window.
// NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
std::optional<nsecs_t> firstDownTimeInTarget;
+ std::string dump() const;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 484b0d3..76dce63 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -125,9 +125,12 @@
/**
* Set the touch mode state.
- * Touch mode is a global state that apps may enter / exit based on specific
- * user interactions with input devices.
- * If true, the device is in touch mode.
+ * Touch mode is a per display state that apps may enter / exit based on specific user
+ * interactions with input devices. If <code>inTouchMode</code> is set to true, the display
+ * identified by <code>displayId</code> will be changed to touch mode. Performs a permission
+ * check if hasPermission is set to false.
+ *
+ * This method also enqueues a a TouchModeEntry message for dispatching.
*
* Returns true when changing touch mode state.
*/
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 8647bcb..1bb1968 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -25,6 +25,8 @@
namespace android {
+std::list<NotifyArgs>& operator+=(std::list<NotifyArgs>& keep, std::list<NotifyArgs>&& consume);
+
/*
* The interface used by the InputReader to notify the InputListener about input events.
*/
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index cacb63c..b8a6dad 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -23,6 +23,7 @@
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
#include <stddef.h>
+#include <ui/Rotation.h>
#include <unistd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -87,6 +88,9 @@
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
int32_t sw) = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/* Toggle Caps Lock */
@@ -142,6 +146,9 @@
virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
/* Get light player ID */
virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
+
+ /* Get the Bluetooth address of an input device, if known. */
+ virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
};
// --- InputReaderConfiguration ---
@@ -392,7 +399,9 @@
/* Gets the affine calibration associated with the specified device. */
virtual TouchAffineTransformation getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
+ /* Notifies the input reader policy that a stylus gesture has started. */
+ virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 611b1aa..c46f905 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -116,6 +116,8 @@
NotifyMotionArgs(const NotifyMotionArgs& other);
+ NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default;
+
bool operator==(const NotifyMotionArgs& rhs) const;
std::string dump() const;
@@ -213,4 +215,6 @@
NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
+const char* toString(const NotifyArgs& args);
+
} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 647e10c..7e0c1c7 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -79,6 +79,8 @@
POINTER,
// Show spots and a spot anchor in place of the mouse pointer.
SPOT,
+
+ ftl_last = SPOT,
};
/* Sets the mode of the pointer controller. */
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 0f87201..f37f0fa 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -23,6 +23,7 @@
cc_library_headers {
name: "libinputreader_headers",
+ host_supported: true,
export_include_dirs: [
"controller",
"include",
@@ -36,11 +37,9 @@
srcs: [
"EventHub.cpp",
"InputDevice.cpp",
+ "InputReader.cpp",
+ "TouchVideoDevice.cpp",
"controller/PeripheralController.cpp",
- "mapper/accumulator/CursorButtonAccumulator.cpp",
- "mapper/accumulator/CursorScrollAccumulator.cpp",
- "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
- "mapper/accumulator/TouchButtonAccumulator.cpp",
"mapper/CursorInputMapper.cpp",
"mapper/ExternalStylusInputMapper.cpp",
"mapper/InputMapper.cpp",
@@ -51,10 +50,17 @@
"mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
"mapper/SwitchInputMapper.cpp",
+ "mapper/TouchCursorInputMapperCommon.cpp",
"mapper/TouchInputMapper.cpp",
+ "mapper/TouchpadInputMapper.cpp",
"mapper/VibratorInputMapper.cpp",
- "InputReader.cpp",
- "TouchVideoDevice.cpp",
+ "mapper/accumulator/CursorButtonAccumulator.cpp",
+ "mapper/accumulator/CursorScrollAccumulator.cpp",
+ "mapper/accumulator/HidUsageAccumulator.cpp",
+ "mapper/accumulator/MultiTouchMotionAccumulator.cpp",
+ "mapper/accumulator/SingleTouchMotionAccumulator.cpp",
+ "mapper/accumulator/TouchButtonAccumulator.cpp",
+ "mapper/gestures/GesturesLogging.cpp",
],
}
@@ -66,24 +72,59 @@
"libcap",
"libcrypto",
"libcutils",
- "libinput",
+ "libjsoncpp",
"liblog",
+ "libPlatformProperties",
"libstatslog",
"libutils",
- "libPlatformProperties",
],
static_libs: [
"libc++fs",
+ "libchrome-gestures",
"libui-types",
],
header_libs: [
"libbatteryservice_headers",
+ "libchrome-gestures_headers",
"libinputreader_headers",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libinput",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libinput",
+ "libbinder",
+ ],
+ },
+ },
+}
+
+cc_library_static {
+ name: "libinputreader_static",
+ defaults: [
+ "inputflinger_defaults",
+ "libinputreader_defaults",
+ ],
+ shared_libs: [
+ "libinputflinger_base",
+ ],
+ export_header_lib_headers: [
+ "libbatteryservice_headers",
+ "libchrome-gestures_headers",
+ "libinputreader_headers",
+ ],
+ whole_static_libs: [
+ "libchrome-gestures",
+ ],
}
cc_library_shared {
name: "libinputreader",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
"libinputreader_defaults",
@@ -95,11 +136,21 @@
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
"libinputflinger_base",
+ "libjsoncpp",
],
export_header_lib_headers: [
"libinputreader_headers",
],
+ target: {
+ host: {
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ },
+ },
static_libs: [
"libc++fs",
+ "libchrome-gestures",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index ca7e426..e26bc8c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/ioctl.h>
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
@@ -28,7 +29,6 @@
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
-#include <sys/limits.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
@@ -43,6 +43,7 @@
#include <ftl/enum.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
+#include <input/PrintTools.h>
#include <input/VirtualKeyMap.h>
#include <openssl/sha.h>
#include <statslog.h>
@@ -134,10 +135,6 @@
{"green", LightColor::GREEN},
{"blue", LightColor::BLUE}};
-static inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
static std::string sha1(const std::string& in) {
SHA_CTX ctx;
SHA1_Init(&ctx);
@@ -152,6 +149,14 @@
return out;
}
+/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */
+static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = {
+ AKEYCODE_STYLUS_BUTTON_PRIMARY,
+ AKEYCODE_STYLUS_BUTTON_SECONDARY,
+ AKEYCODE_STYLUS_BUTTON_TERTIARY,
+ AKEYCODE_STYLUS_BUTTON_TAIL,
+};
+
/**
* Return true if name matches "v4l-touch*"
*/
@@ -192,8 +197,8 @@
// calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
// system call that also queries ktime_get_ts().
- const nsecs_t inputEventTime = seconds_to_nanoseconds(event.time.tv_sec) +
- microseconds_to_nanoseconds(event.time.tv_usec);
+ const nsecs_t inputEventTime = seconds_to_nanoseconds(event.input_event_sec) +
+ microseconds_to_nanoseconds(event.input_event_usec);
return inputEventTime;
}
@@ -633,8 +638,8 @@
int32_t sc;
if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) {
struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
+ ev.input_event_sec = 0;
+ ev.input_event_usec = 0;
ev.type = EV_LED;
ev.code = sc;
ev.value = on ? 1 : 0;
@@ -947,15 +952,19 @@
device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode);
switch (mapKeyRes) {
case OK:
- return outKeyCode;
+ break;
case NAME_NOT_FOUND:
// key character map doesn't re-map this scanCode, hence the keyCode remains the same
- return locationKeyCode;
+ outKeyCode = locationKeyCode;
+ break;
default:
ALOGW("Failed to get key code for key location: Key character map returned error %s",
statusToString(mapKeyRes).c_str());
- return AKEYCODE_UNKNOWN;
+ outKeyCode = AKEYCODE_UNKNOWN;
+ break;
}
+ // Remap if there is a Key remapping added to the KCM and return the remapped key
+ return device->getKeyCharacterMap()->applyKeyRemapping(outKeyCode);
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
@@ -1018,6 +1027,18 @@
return false;
}
+void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ return;
+ }
+ const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+ if (kcm) {
+ kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
std::scoped_lock _l(mLock);
@@ -1043,7 +1064,13 @@
if (status == NO_ERROR) {
if (kcm) {
- kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
+ // Remap keys based on user-defined key remappings and key behavior defined in the
+ // corresponding kcm file
+ *outKeycode = kcm->applyKeyRemapping(*outKeycode);
+
+ // Remap keys based on Key behavior defined in KCM file
+ std::tie(*outKeycode, *outMetaState) =
+ kcm->applyKeyBehavior(*outKeycode, metaState);
} else {
*outMetaState = metaState;
}
@@ -1463,8 +1490,8 @@
device->ffEffectId = effect.id;
struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
+ ev.input_event_sec = 0;
+ ev.input_event_usec = 0;
ev.type = EV_FF;
ev.code = device->ffEffectId;
ev.value = 1;
@@ -1485,8 +1512,8 @@
device->ffEffectPlaying = false;
struct input_event ev;
- ev.time.tv_sec = 0;
- ev.time.tv_usec = 0;
+ ev.input_event_sec = 0;
+ ev.input_event_usec = 0;
ev.type = EV_FF;
ev.code = device->ffEffectId;
ev.value = 0;
@@ -2128,6 +2155,17 @@
identifier.uniqueId = buffer;
}
+ // Attempt to get the bluetooth address of an input device from the uniqueId.
+ if (identifier.bus == BUS_BLUETOOTH &&
+ std::regex_match(identifier.uniqueId,
+ std::regex("^[A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){5}$"))) {
+ identifier.bluetoothAddress = identifier.uniqueId;
+ // The Bluetooth stack requires alphabetic characters to be uppercase in a valid address.
+ for (auto& c : *identifier.bluetoothAddress) {
+ c = ::toupper(c);
+ }
+ }
+
// Fill in the descriptor.
assignDescriptorLocked(identifier);
@@ -2163,13 +2201,15 @@
device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
- // See if this is a keyboard. Ignore everything in the button range except for
- // joystick and gamepad buttons which are handled like keyboards for the most part.
+ // See if this is a device with keys. This could be full keyboard, or other devices like
+ // gamepads, joysticks, and styluses with buttons that should generate key presses.
bool haveKeyboardKeys =
device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
- if (haveKeyboardKeys || haveGamepadButtons) {
+ bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) ||
+ device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3);
+ if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) {
device->classes |= InputDeviceClass::KEYBOARD;
}
@@ -2179,11 +2219,13 @@
device->classes |= InputDeviceClass::CURSOR;
}
- // See if this is a rotary encoder type device.
+ // See if the device is specially configured to be of a certain type.
std::string deviceType;
if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
if (deviceType == "rotaryEncoder") {
device->classes |= InputDeviceClass::ROTARY_ENCODER;
+ } else if (deviceType == "externalStylus") {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
}
}
@@ -2195,19 +2237,19 @@
// a touch screen.
if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
+ if (device->propBitmask.test(INPUT_PROP_POINTER) &&
+ !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
+ device->classes |= InputDeviceClass::TOUCHPAD;
+ }
}
// Is this an old style single-touch driver?
} else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
device->absBitmask.test(ABS_Y)) {
device->classes |= InputDeviceClass::TOUCH;
- // Is this a BT stylus?
+ // Is this a stylus that reports contact/pressure independently of touch coordinates?
} else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
!device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
- // Keyboard will try to claim some of the buttons but we really want to reserve those so we
- // can fuse it with the touch screen data, so just take them back. Note this means an
- // external stylus cannot also be a keyboard device.
- device->classes &= ~InputDeviceClass::KEYBOARD;
}
// See if this device is a joystick.
@@ -2292,6 +2334,16 @@
break;
}
}
+
+ // See if this device has any stylus buttons that we would want to fuse with touch data.
+ if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) {
+ for (int32_t keycode : STYLUS_BUTTON_KEYCODES) {
+ if (device->hasKeycodeLocked(keycode)) {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
+ break;
+ }
+ }
+ }
}
// If the device isn't recognized as something we handle, don't monitor it.
@@ -2625,9 +2677,10 @@
dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
- "product=0x%04x, version=0x%04x\n",
+ "product=0x%04x, version=0x%04x, bluetoothAddress=%s\n",
device->identifier.bus, device->identifier.vendor,
- device->identifier.product, device->identifier.version);
+ device->identifier.product, device->identifier.version,
+ toString(device->identifier.bluetoothAddress).c_str());
dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
device->keyMap.keyLayoutFile.c_str());
dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6b9b9f1..11b5209 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
+#include <android/sysprop/InputProperties.sysprop.h>
#include <ftl/flags.h>
#include "CursorInputMapper.h"
@@ -33,6 +34,7 @@
#include "SensorInputMapper.h"
#include "SingleTouchInputMapper.h"
#include "SwitchInputMapper.h"
+#include "TouchpadInputMapper.h"
#include "VibratorInputMapper.h"
using android::hardware::input::InputDeviceCountryCode;
@@ -65,7 +67,8 @@
return enabled;
}
-void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+std::list<NotifyArgs> InputDevice::setEnabled(bool enabled, nsecs_t when) {
+ std::list<NotifyArgs> out;
if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
"but the corresponding viewport is not found",
@@ -74,7 +77,7 @@
}
if (isEnabled() == enabled) {
- return;
+ return out;
}
// When resetting some devices, the driver needs to be queried to ensure that a proper reset is
@@ -82,13 +85,14 @@
// but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
if (enabled) {
for_each_subdevice([](auto& context) { context.enableDevice(); });
- reset(when);
+ out += reset(when);
} else {
- reset(when);
+ out += reset(when);
for_each_subdevice([](auto& context) { context.disableDevice(); });
}
// Must change generation to flag this device as changed
bumpGeneration();
+ return out;
}
void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
@@ -206,7 +210,12 @@
}
// Touchscreens and touchpad devices.
- if (classes.test(InputDeviceClass::TOUCH_MT)) {
+ static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
+ sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+ if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
+ classes.test(InputDeviceClass::TOUCH_MT)) {
+ mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
+ } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
} else if (classes.test(InputDeviceClass::TOUCH)) {
mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
@@ -241,8 +250,9 @@
mDevices.erase(eventHubId);
}
-void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
+std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out;
mSources = 0;
mClasses = ftl::Flags<InputDeviceClass>(0);
mControllerNumber = 0;
@@ -310,10 +320,13 @@
}
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
+ if (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE) {
+ // Do not execute this code on the first configure, because 'setEnabled' would call
+ // InputMapper::reset, and you can't reset a mapper before it has been configured.
+ // The mappers are configured for the first time at the bottom of this function.
auto it = config->disabledDevices.find(mId);
bool enabled = it == config->disabledDevices.end();
- setEnabled(enabled, when);
+ out += setEnabled(enabled, when);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
@@ -366,37 +379,42 @@
// For first-time configuration, only allow device to be disabled after mappers have
// finished configuring. This is because we need to read some of the properties from
// the device's open fd.
- setEnabled(enabled, when);
+ out += setEnabled(enabled, when);
}
}
- for_each_mapper([this, when, config, changes](InputMapper& mapper) {
- mapper.configure(when, config, changes);
+ for_each_mapper([this, when, &config, changes, &out](InputMapper& mapper) {
+ out += mapper.configure(when, config, changes);
mSources |= mapper.getSources();
});
// If a device is just plugged but it might be disabled, we need to update some info like
// axis range of touch from each InputMapper first, then disable it.
if (!changes) {
- setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
+ out += setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(),
+ when);
}
}
+ return out;
}
-void InputDevice::reset(nsecs_t when) {
- for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); });
+std::list<NotifyArgs> InputDevice::reset(nsecs_t when) {
+ std::list<NotifyArgs> out;
+ for_each_mapper([&](InputMapper& mapper) { out += mapper.reset(when); });
mContext->updateGlobalMetaState();
- notifyReset(when);
+ out.push_back(notifyReset(when));
+ return out;
}
-void InputDevice::process(const RawEvent* rawEvents, size_t count) {
+std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
+ std::list<NotifyArgs> out;
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
if (DEBUG_RAW_EVENTS) {
ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
@@ -418,22 +436,27 @@
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
mDropUntilNextSync = true;
- reset(rawEvent->when);
+ out += reset(rawEvent->when);
} else {
- for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
- mapper.process(rawEvent);
+ for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
+ out += mapper.process(rawEvent);
});
}
--count;
}
+ return out;
}
-void InputDevice::timeoutExpired(nsecs_t when) {
- for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); });
+std::list<NotifyArgs> InputDevice::timeoutExpired(nsecs_t when) {
+ std::list<NotifyArgs> out;
+ for_each_mapper([&](InputMapper& mapper) { out += mapper.timeoutExpired(when); });
+ return out;
}
-void InputDevice::updateExternalStylusState(const StylusState& state) {
- for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
+std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& state) {
+ std::list<NotifyArgs> out;
+ for_each_mapper([&](InputMapper& mapper) { out += mapper.updateExternalStylusState(state); });
+ return out;
}
InputDeviceInfo InputDevice::getDeviceInfo() {
@@ -511,14 +534,17 @@
return *result;
}
-void InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {
- for_each_mapper([sequence, repeat, token](InputMapper& mapper) {
- mapper.vibrate(sequence, repeat, token);
- });
+std::list<NotifyArgs> InputDevice::vibrate(const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) {
+ std::list<NotifyArgs> out;
+ for_each_mapper([&](InputMapper& mapper) { out += mapper.vibrate(sequence, repeat, token); });
+ return out;
}
-void InputDevice::cancelVibrate(int32_t token) {
- for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
+std::list<NotifyArgs> InputDevice::cancelVibrate(int32_t token) {
+ std::list<NotifyArgs> out;
+ for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelVibrate(token); });
+ return out;
}
bool InputDevice::isVibrating() {
@@ -561,8 +587,10 @@
for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
}
-void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
- for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
+std::list<NotifyArgs> InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ for_each_mapper([&](InputMapper& mapper) { out += mapper.cancelTouch(when, readTime); });
+ return out;
}
bool InputDevice::setLightColor(int32_t lightId, int32_t color) {
@@ -597,13 +625,18 @@
});
}
+void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
+ context.addKeyRemapping(fromKeyCode, toKeyCode);
+ });
+}
+
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
-void InputDevice::notifyReset(nsecs_t when) {
- NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
- mContext->getListener().notifyDeviceReset(&args);
+NotifyDeviceResetArgs InputDevice::notifyReset(nsecs_t when) {
+ return NotifyDeviceResetArgs(mContext->getNextId(), when, mId);
}
std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 8650876..57f679c 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -58,6 +58,17 @@
identifier1.location == identifier2.location);
}
+static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
+ const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
+ if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
+ actionMasked != AMOTION_EVENT_ACTION_DOWN &&
+ actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ return false;
+ }
+ const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
+ return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType);
+}
+
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -101,8 +112,10 @@
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
+ // Copy some state so that we can access it outside the lock later.
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
+ std::list<NotifyArgs> notifyArgs;
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -127,7 +140,7 @@
mReaderIsAliveCondition.notify_all();
if (!events.empty()) {
- processEventsLocked(events.data(), events.size());
+ notifyArgs += processEventsLocked(events.data(), events.size());
}
if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +150,7 @@
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
}
mNextTimeout = LLONG_MAX;
- timeoutExpiredLocked(now);
+ notifyArgs += timeoutExpiredLocked(now);
}
}
@@ -152,6 +165,16 @@
mPolicy->notifyInputDevicesChanged(inputDevices);
}
+ // Notify the policy of the start of every new stylus gesture outside the lock.
+ for (const auto& args : notifyArgs) {
+ const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
+ if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
+ mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);
+ }
+ }
+
+ notifyAll(std::move(notifyArgs));
+
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -162,7 +185,8 @@
mQueuedListener.flush();
}
-void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
+ std::list<NotifyArgs> out;
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
@@ -178,7 +202,7 @@
if (DEBUG_RAW_EVENTS) {
ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
}
- processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
+ out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
@@ -198,6 +222,7 @@
count -= batchSize;
rawEvent += batchSize;
}
+ return out;
}
void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
@@ -208,8 +233,9 @@
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
- device->configure(when, &mConfig, 0);
- device->reset(when);
+
+ notifyAll(device->configure(when, &mConfig, 0));
+ notifyAll(device->reset(when));
if (device->isIgnored()) {
ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
@@ -282,10 +308,12 @@
notifyExternalStylusPresenceChangedLocked();
}
+ std::list<NotifyArgs> resetEvents;
if (device->hasEventHubDevices()) {
- device->configure(when, &mConfig, 0);
+ resetEvents += device->configure(when, &mConfig, 0);
}
- device->reset(when);
+ resetEvents += device->reset(when);
+ notifyAll(std::move(resetEvents));
}
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
@@ -308,21 +336,22 @@
return device;
}
-void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
- size_t count) {
+std::list<NotifyArgs> InputReader::processEventsForDeviceLocked(int32_t eventHubId,
+ const RawEvent* rawEvents,
+ size_t count) {
auto deviceIt = mDevices.find(eventHubId);
if (deviceIt == mDevices.end()) {
ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
- return;
+ return {};
}
std::shared_ptr<InputDevice>& device = deviceIt->second;
if (device->isIgnored()) {
// ALOGD("Discarding event for ignored deviceId %d.", deviceId);
- return;
+ return {};
}
- device->process(rawEvents, count);
+ return device->process(rawEvents, count);
}
InputDevice* InputReader::findInputDeviceLocked(int32_t deviceId) const {
@@ -336,13 +365,15 @@
return nullptr;
}
-void InputReader::timeoutExpiredLocked(nsecs_t when) {
+std::list<NotifyArgs> InputReader::timeoutExpiredLocked(nsecs_t when) {
+ std::list<NotifyArgs> out;
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
if (!device->isIgnored()) {
- device->timeoutExpired(when);
+ out += device->timeoutExpired(when);
}
}
+ return out;
}
int32_t InputReader::nextInputDeviceIdLocked() {
@@ -377,7 +408,7 @@
} else {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
- device->configure(now, &mConfig, changes);
+ notifyAll(device->configure(now, &mConfig, changes));
}
}
@@ -394,6 +425,12 @@
}
}
+void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
+ for (const NotifyArgs& args : argsList) {
+ mQueuedListener.notify(args);
+ }
+}
+
void InputReader::updateGlobalMetaStateLocked() {
mGlobalMetaState = 0;
@@ -432,11 +469,13 @@
}
}
-void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
+std::list<NotifyArgs> InputReader::dispatchExternalStylusStateLocked(const StylusState& state) {
+ std::list<NotifyArgs> out;
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
- device->updateExternalStylusState(state);
+ out += device->updateExternalStylusState(state);
}
+ return out;
}
void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
@@ -611,6 +650,15 @@
return result;
}
+void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device != nullptr) {
+ device->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
std::scoped_lock _l(mLock);
@@ -642,7 +690,7 @@
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- device->vibrate(sequence, repeat, token);
+ notifyAll(device->vibrate(sequence, repeat, token));
}
}
@@ -651,7 +699,7 @@
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- device->cancelVibrate(token);
+ notifyAll(device->cancelVibrate(token));
}
}
@@ -835,6 +883,16 @@
return std::nullopt;
}
+std::optional<std::string> InputReader::getBluetoothAddress(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getBluetoothAddress();
+ }
+ return std::nullopt;
+}
+
bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
std::scoped_lock _l(mLock);
@@ -1015,18 +1073,15 @@
mReader->getExternalStylusDevicesLocked(outDevices);
}
-void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) {
- mReader->dispatchExternalStylusStateLocked(state);
+std::list<NotifyArgs> InputReader::ContextImpl::dispatchExternalStylusState(
+ const StylusState& state) {
+ return mReader->dispatchExternalStylusStateLocked(state);
}
InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
return mReader->mPolicy.get();
}
-InputListenerInterface& InputReader::ContextImpl::getListener() {
- return mReader->mQueuedListener;
-}
-
EventHubInterface* InputReader::ContextImpl::getEventHub() {
return mReader->mEventHub.get();
}
diff --git a/services/inputflinger/reader/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp
index 2f8138b..627dcba 100644
--- a/services/inputflinger/reader/TouchVideoDevice.cpp
+++ b/services/inputflinger/reader/TouchVideoDevice.cpp
@@ -198,8 +198,9 @@
if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) {
// We use CLOCK_MONOTONIC for input events, so if the clocks don't match,
// we can't compare timestamps. Just log a warning, since this is a driver issue
- ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec,
- buf.timestamp.tv_usec);
+ ALOGW("The timestamp %lld.%lld was not acquired using CLOCK_MONOTONIC",
+ static_cast<long long>(buf.timestamp.tv_sec),
+ static_cast<long long>(buf.timestamp.tv_usec));
}
std::vector<int16_t> data(mHeight * mWidth);
const int16_t* readFrom = mReadLocations[buf.index];
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 6933ec7..8a844b2 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -67,22 +67,15 @@
/* Describes an absolute axis. */
struct RawAbsoluteAxisInfo {
- bool valid; // true if the information is valid, false otherwise
+ bool valid{false}; // true if the information is valid, false otherwise
- int32_t minValue; // minimum value
- int32_t maxValue; // maximum value
- int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
- int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
- int32_t resolution; // resolution in units per mm or radians per mm
+ int32_t minValue{}; // minimum value
+ int32_t maxValue{}; // maximum value
+ int32_t flat{}; // center flat position, eg. flat == 8 means center is between -8 and 8
+ int32_t fuzz{}; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+ int32_t resolution{}; // resolution in units per mm or radians per mm
- inline void clear() {
- valid = false;
- minValue = 0;
- maxValue = 0;
- flat = 0;
- fuzz = 0;
- resolution = 0;
- }
+ inline void clear() { *this = RawAbsoluteAxisInfo(); }
};
/*
@@ -101,7 +94,7 @@
/* The input device is a cursor device such as a trackball or mouse. */
CURSOR = 0x00000008,
- /* The input device is a multi-touch touchscreen. */
+ /* The input device is a multi-touch touchscreen or touchpad. */
TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
@@ -137,6 +130,9 @@
/* The input device has sysfs controllable lights */
LIGHT = 0x00008000,
+ /* The input device is a touchpad, requiring an on-screen cursor. */
+ TOUCHPAD = 0x00010000,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
VIRTUAL = 0x40000000,
@@ -266,6 +262,9 @@
virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const = 0;
@@ -464,6 +463,9 @@
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const override final;
+
status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4ae9ae9..6fa21e5 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -29,6 +29,7 @@
#include "EventHub.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
+#include "NotifyArgs.h"
namespace android {
@@ -50,6 +51,9 @@
inline int32_t getGeneration() const { return mGeneration; }
inline const std::string getName() const { return mIdentifier.name; }
inline const std::string getDescriptor() { return mIdentifier.descriptor; }
+ inline std::optional<std::string> getBluetoothAddress() const {
+ return mIdentifier.bluetoothAddress;
+ }
inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
@@ -69,16 +73,18 @@
inline bool isIgnored() { return !getMapperCount(); }
bool isEnabled();
- void setEnabled(bool enabled, nsecs_t when);
+ [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when);
void dump(std::string& dump, const std::string& eventHubDevStr);
void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
void removeEventHubDevice(int32_t eventHubId);
- void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- void reset(nsecs_t when);
- void process(const RawEvent* rawEvents, size_t count);
- void timeoutExpired(nsecs_t when);
- void updateExternalStylusState(const StylusState& state);
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes);
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count);
+ [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when);
+ [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
InputDeviceInfo getDeviceInfo();
int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
@@ -87,11 +93,12 @@
int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const;
bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags);
- void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
- void cancelVibrate(int32_t token);
+ [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token);
+ [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token);
bool isVibrating();
std::vector<int32_t> getVibratorIds();
- void cancelTouch(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime);
bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency);
void disableSensor(InputDeviceSensorType sensorType);
@@ -107,9 +114,11 @@
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
void bumpGeneration();
- void notifyReset(nsecs_t when);
+ [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
inline const PropertyMap& getConfiguration() { return mConfiguration; }
inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
@@ -271,6 +280,10 @@
inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
+ inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
+ mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+ }
+
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
@@ -371,8 +384,11 @@
mEventHub->getAbsoluteAxisInfo(mId, code, &info);
return info.valid;
}
- inline bool isKeyPressed(int32_t code) const {
- return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+ inline bool isKeyPressed(int32_t scanCode) const {
+ return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
+ }
+ inline bool isKeyCodePressed(int32_t keyCode) const {
+ return mEventHub->getKeyCodeState(mId, keyCode) == AKEY_STATE_DOWN;
}
inline int32_t getAbsoluteAxisValue(int32_t code) const {
int32_t value;
@@ -395,7 +411,9 @@
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mDevice.getAssociatedViewport();
}
- inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); }
+ [[nodiscard]] inline std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) {
+ return mDevice.cancelTouch(when, readTime);
+ }
inline void bumpGeneration() { mDevice.bumpGeneration(); }
inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 012d43f..e9c989a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -68,6 +68,8 @@
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
void toggleCapsLockState(int32_t deviceId) override;
@@ -113,6 +115,8 @@
std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override;
+ std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
@@ -142,10 +146,9 @@
int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override;
void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices)
REQUIRES(mReader->mLock) override;
- void dispatchExternalStylusState(const StylusState& outState)
+ [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState)
REQUIRES(mReader->mLock) override;
InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override;
- InputListenerInterface& getListener() REQUIRES(mReader->mLock) override;
EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override;
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
@@ -181,13 +184,15 @@
mDeviceToEventHubIdsMap GUARDED_BY(mLock);
// low-level input event decoding and device management
- void processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock);
+ [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
+ REQUIRES(mLock);
void addDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
void removeDeviceLocked(nsecs_t when, int32_t eventHubId) REQUIRES(mLock);
- void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count)
- REQUIRES(mLock);
- void timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
+ [[nodiscard]] std::list<NotifyArgs> processEventsForDeviceLocked(int32_t eventHubId,
+ const RawEvent* rawEvents,
+ size_t count) REQUIRES(mLock);
+ [[nodiscard]] std::list<NotifyArgs> timeoutExpiredLocked(nsecs_t when) REQUIRES(mLock);
void handleConfigurationChangedLocked(nsecs_t when) REQUIRES(mLock);
@@ -201,7 +206,8 @@
void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock);
void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock);
- void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock);
+ [[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusStateLocked(const StylusState& state)
+ REQUIRES(mLock);
// The PointerController that is shared among all the input devices that need it.
std::weak_ptr<PointerControllerInterface> mPointerController;
@@ -228,6 +234,8 @@
uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
+ void notifyAll(std::list<NotifyArgs>&& argsList);
+
PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
// state queries
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index f2f156c..0beace1 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -17,6 +17,7 @@
#pragma once
#include <input/InputDevice.h>
+#include "NotifyArgs.h"
#include <vector>
@@ -51,10 +52,10 @@
virtual int32_t bumpGeneration() = 0;
virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0;
- virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
+ [[nodiscard]] virtual std::list<NotifyArgs> dispatchExternalStylusState(
+ const StylusState& outState) = 0;
virtual InputReaderPolicyInterface* getPolicy() = 0;
- virtual InputListenerInterface& getListener() = 0;
virtual EventHubInterface* getEventHub() = 0;
virtual int32_t getNextId() = 0;
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
index 8d14d3c..ff15e0c 100644
--- a/services/inputflinger/reader/include/StylusState.h
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -24,27 +24,19 @@
struct StylusState {
/* Time the stylus event was received. */
- nsecs_t when;
- /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
- float pressure;
+ nsecs_t when{};
+ /*
+ * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0].
+ * The presence of a pressure value indicates that the stylus is able to tell whether it is
+ * touching the display.
+ */
+ std::optional<float> pressure{};
/* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
- uint32_t buttons;
+ uint32_t buttons{};
/* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
- int32_t toolType;
+ int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN};
- void copyFrom(const StylusState& other) {
- when = other.when;
- pressure = other.pressure;
- buttons = other.buttons;
- toolType = other.toolType;
- }
-
- void clear() {
- when = LLONG_MAX;
- pressure = 0.f;
- buttons = 0;
- toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
- }
+ void clear() { *this = StylusState{}; }
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index d6d324b..13e4d0c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -67,7 +67,7 @@
// --- CursorInputMapper ---
CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext) {}
+ : InputMapper(deviceContext), mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
CursorInputMapper::~CursorInputMapper() {
if (mPointerController != nullptr) {
@@ -117,6 +117,10 @@
toString(mCursorScrollAccumulator.haveRelativeVWheel()));
dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
toString(mCursorScrollAccumulator.haveRelativeHWheel()));
+ dump += StringPrintf(INDENT3 "WheelYVelocityControlParameters: %s",
+ mWheelYVelocityControl.getParameters().dump().c_str());
+ dump += StringPrintf(INDENT3 "WheelXVelocityControlParameters: %s",
+ mWheelXVelocityControl.getParameters().dump().c_str());
dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
@@ -126,9 +130,10 @@
dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
}
-void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> CursorInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
if (!changes) { // first time only
mCursorScrollAccumulator.configure(getDeviceContext());
@@ -187,8 +192,7 @@
}
bumpGeneration();
if (changes) {
- NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
- getListener().notifyDeviceReset(&args);
+ out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
}
@@ -223,7 +227,7 @@
mDisplayId = mPointerController->getDisplayId();
}
- mOrientation = DISPLAY_ORIENTATION_0;
+ 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
@@ -241,6 +245,7 @@
bumpGeneration();
}
+ return out;
}
void CursorInputMapper::configureParameters() {
@@ -272,9 +277,10 @@
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
}
-void CursorInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) {
mButtonState = 0;
mDownTime = 0;
+ mLastEventTime = std::numeric_limits<nsecs_t>::min();
mPointerVelocityControl.reset();
mWheelXVelocityControl.reset();
@@ -284,23 +290,31 @@
mCursorMotionAccumulator.reset(getDeviceContext());
mCursorScrollAccumulator.reset(getDeviceContext());
- InputMapper::reset(when);
+ return InputMapper::reset(when);
}
-void CursorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
mCursorButtonAccumulator.process(rawEvent);
mCursorMotionAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when, rawEvent->readTime);
+ const auto [eventTime, readTime] =
+ applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(),
+ rawEvent->when, rawEvent->readTime,
+ mLastEventTime);
+ out += sync(eventTime, readTime);
+ mLastEventTime = eventTime;
}
+ return out;
}
-void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
if (!mDisplayId) {
// Ignore events when there is no target display configured.
- return;
+ return out;
}
int32_t lastButtonState = mButtonState;
@@ -391,8 +405,9 @@
}
// Synthesize key down from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
- mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
+ out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+ mSource, *mDisplayId, policyFlags, lastButtonState,
+ currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
@@ -412,40 +427,38 @@
while (!released.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
- NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
- getDeviceId(), mSource, *mDisplayId, policyFlags,
- AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener().notifyMotion(&releaseArgs);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime,
+ getDeviceId(), mSource, *mDisplayId, policyFlags,
+ AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {}));
}
}
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
- currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
- mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, *mDisplayId, policyFlags, motionEventAction, 0, 0,
+ metaState, currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+ yCursorPosition, downTime,
+ /* videoFrames */ {}));
if (buttonsPressed) {
BitSet32 pressed(buttonsPressed);
while (!pressed.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
- NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, *mDisplayId, policyFlags,
- AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
- /* videoFrames */ {});
- getListener().notifyMotion(&pressArgs);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime,
+ getDeviceId(), mSource, *mDisplayId, policyFlags,
+ AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {}));
}
}
@@ -453,14 +466,14 @@
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
- NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, *mDisplayId, policyFlags,
- AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
- yCursorPosition, downTime, /* videoFrames */ {});
- getListener().notifyMotion(&hoverArgs);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, *mDisplayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {}));
}
// Send scroll events.
@@ -468,23 +481,25 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, *mDisplayId, policyFlags,
- AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
- currentButtonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
- yCursorPosition, downTime, /* videoFrames */ {});
- getListener().notifyMotion(&scrollArgs);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, *mDisplayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, mXPrecision, mYPrecision,
+ xCursorPosition, yCursorPosition, downTime,
+ /* videoFrames */ {}));
}
}
// Synthesize key up from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
- *mDisplayId, policyFlags, lastButtonState, currentButtonState);
+ out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(),
+ mSource, *mDisplayId, policyFlags, lastButtonState,
+ currentButtonState);
mCursorMotionAccumulator.finishSync();
mCursorScrollAccumulator.finishSync();
+ return out;
}
int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index a0229a7..939cceb 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -22,6 +22,7 @@
#include <PointerControllerInterface.h>
#include <input/VelocityControl.h>
+#include <ui/Rotation.h>
namespace android {
@@ -58,10 +59,11 @@
virtual uint32_t getSources() const override;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
@@ -114,17 +116,18 @@
// 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;
- int32_t mOrientation;
+ ui::Rotation mOrientation;
std::shared_ptr<PointerControllerInterface> mPointerController;
int32_t mButtonState;
nsecs_t mDownTime;
+ nsecs_t mLastEventTime;
void configureParameters();
void dumpParameters(std::string& dump);
- void sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 6b5d37f..2809939 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -24,7 +24,7 @@
namespace android {
ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext) {}
+ : InputMapper(deviceContext), mTouchButtonAccumulator(deviceContext) {}
uint32_t ExternalStylusInputMapper::getSources() const {
return AINPUT_SOURCE_STYLUS;
@@ -32,8 +32,10 @@
void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
+ if (mRawPressureAxis.valid) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f);
+ }
}
void ExternalStylusInputMapper::dump(std::string& dump) {
@@ -44,28 +46,32 @@
dumpStylusState(dump, mStylusState);
}
-void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
+std::list<NotifyArgs> ExternalStylusInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
- mTouchButtonAccumulator.configure(getDeviceContext());
+ mTouchButtonAccumulator.configure();
+ return {};
}
-void ExternalStylusInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> ExternalStylusInputMapper::reset(nsecs_t when) {
mSingleTouchMotionAccumulator.reset(getDeviceContext());
- mTouchButtonAccumulator.reset(getDeviceContext());
- InputMapper::reset(when);
+ mTouchButtonAccumulator.reset();
+ return InputMapper::reset(when);
}
-void ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
mSingleTouchMotionAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ out += sync(rawEvent->when);
}
+ return out;
}
-void ExternalStylusInputMapper::sync(nsecs_t when) {
+std::list<NotifyArgs> ExternalStylusInputMapper::sync(nsecs_t when) {
mStylusState.clear();
mStylusState.when = when;
@@ -75,18 +81,17 @@
mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
if (mRawPressureAxis.valid) {
- mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
- } else if (mTouchButtonAccumulator.isToolActive()) {
- mStylusState.pressure = 1.0f;
- } else {
- mStylusState.pressure = 0.0f;
+ auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
+ mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
+ static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
+ } else if (mTouchButtonAccumulator.hasButtonTouch()) {
+ mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
}
mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
- getContext()->dispatchExternalStylusState(mStylusState);
+ return getContext()->dispatchExternalStylusState(mStylusState);
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 03d9909..b6c9055 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -29,13 +29,14 @@
explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext);
virtual ~ExternalStylusInputMapper() = default;
- virtual uint32_t getSources() const override;
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
- virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ uint32_t getSources() const override;
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void dump(std::string& dump) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
@@ -44,7 +45,7 @@
StylusState mStylusState;
- void sync(nsecs_t when);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 75cebf3..8e3539c 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -19,6 +19,7 @@
#include "InputMapper.h"
#include "InputDevice.h"
+#include "input/PrintTools.h"
namespace android {
@@ -32,12 +33,18 @@
void InputMapper::dump(std::string& dump) {}
-void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {}
+std::list<NotifyArgs> InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+ uint32_t changes) {
+ return {};
+}
-void InputMapper::reset(nsecs_t when) {}
+std::list<NotifyArgs> InputMapper::reset(nsecs_t when) {
+ return {};
+}
-void InputMapper::timeoutExpired(nsecs_t when) {}
+std::list<NotifyArgs> InputMapper::timeoutExpired(nsecs_t when) {
+ return {};
+}
int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
return AKEY_STATE_UNKNOWN;
@@ -60,9 +67,14 @@
return false;
}
-void InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) {}
+std::list<NotifyArgs> InputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) {
+ return {};
+}
-void InputMapper::cancelVibrate(int32_t token) {}
+std::list<NotifyArgs> InputMapper::cancelVibrate(int32_t token) {
+ return {};
+}
bool InputMapper::isVibrating() {
return false;
@@ -72,7 +84,9 @@
return {};
}
-void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {}
+std::list<NotifyArgs> InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ return {};
+}
bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
@@ -92,7 +106,9 @@
return false;
}
-void InputMapper::updateExternalStylusState(const StylusState& state) {}
+std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) {
+ return {};
+}
status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
@@ -114,7 +130,7 @@
void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
- dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
+ dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str());
dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 5567cac..104305b 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -20,6 +20,7 @@
#include "InputDevice.h"
#include "InputListener.h"
#include "InputReaderContext.h"
+#include "NotifyArgs.h"
#include "StylusState.h"
#include "VibrationElement.h"
@@ -48,15 +49,16 @@
inline const std::string getDeviceName() const { return mDeviceContext.getName(); }
inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
- inline InputListenerInterface& getListener() { return getContext()->getListener(); }
virtual uint32_t getSources() const = 0;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
virtual void dump(std::string& dump);
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
- virtual void reset(nsecs_t when);
- virtual void process(const RawEvent* rawEvent) = 0;
- virtual void timeoutExpired(nsecs_t when);
+ [[nodiscard]] virtual std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes);
+ [[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
+ [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
+ [[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
@@ -65,11 +67,12 @@
virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags);
- virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token);
- virtual void cancelVibrate(int32_t token);
+ [[nodiscard]] virtual std::list<NotifyArgs> vibrate(const VibrationSequence& sequence,
+ ssize_t repeat, int32_t token);
+ [[nodiscard]] virtual std::list<NotifyArgs> cancelVibrate(int32_t token);
virtual bool isVibrating();
virtual std::vector<int32_t> getVibratorIds();
- virtual void cancelTouch(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] virtual std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime);
virtual bool enableSensor(InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency);
@@ -91,7 +94,7 @@
*/
virtual bool updateMetaState(int32_t keyCode);
- virtual void updateExternalStylusState(const StylusState& state);
+ [[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
virtual void updateLedState(bool reset) {}
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 7d30d0c..929bf18 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -103,9 +103,10 @@
}
}
-void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> JoystickInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
if (!changes) { // first time only
// Collect all axes.
@@ -145,12 +146,12 @@
for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) {
Axis& axis = it->second;
if (axis.axisInfo.axis < 0) {
- while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 &&
+ while (nextGenericAxisId <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE &&
haveAxis(nextGenericAxisId)) {
nextGenericAxisId += 1;
}
- if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
+ if (nextGenericAxisId <= AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) {
axis.axisInfo.axis = nextGenericAxisId;
nextGenericAxisId += 1;
} else {
@@ -164,6 +165,7 @@
it++;
}
}
+ return out;
}
JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo,
@@ -246,17 +248,18 @@
}
}
-void JoystickInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> JoystickInputMapper::reset(nsecs_t when) {
// Recenter all axes.
for (std::pair<const int32_t, Axis>& pair : mAxes) {
Axis& axis = pair.second;
axis.resetValue();
}
- InputMapper::reset(when);
+ return InputMapper::reset(when);
}
-void JoystickInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
switch (rawEvent->type) {
case EV_ABS: {
auto it = mAxes.find(rawEvent->code);
@@ -298,16 +301,18 @@
case EV_SYN:
switch (rawEvent->code) {
case SYN_REPORT:
- sync(rawEvent->when, rawEvent->readTime, false /*force*/);
+ out += sync(rawEvent->when, rawEvent->readTime, false /*force*/);
break;
}
break;
}
+ return out;
}
-void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
+std::list<NotifyArgs> JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
+ std::list<NotifyArgs> out;
if (!filterAxes(force)) {
- return;
+ return out;
}
int32_t metaState = getContext()->getGlobalMetaState();
@@ -340,13 +345,14 @@
displayId = getDeviceContext().getAssociatedViewport()->displayId;
}
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
- AINPUT_SOURCE_JOYSTICK, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE,
- 0, 0, metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ AINPUT_SOURCE_JOYSTICK, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &pointerProperties, &pointerCoords, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
+ return out;
}
void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index e002397..72b8a52 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -28,10 +28,11 @@
virtual uint32_t getSources() const override;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
private:
struct Axis {
@@ -91,7 +92,7 @@
// Axes indexed by raw ABS_* axis index.
std::unordered_map<int32_t, Axis> mAxes;
- void sync(nsecs_t when, nsecs_t readTime, bool force);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime, bool force);
bool haveAxis(int32_t axisId);
void pruneAxes(bool ignoreExplicitlyMappedAxes);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 9bb6273..44f0dfe 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -20,71 +20,75 @@
#include "KeyboardInputMapper.h"
+#include <ui/Rotation.h>
+
namespace android {
// --- Static Definitions ---
-static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
- const int32_t map[][4], size_t mapSize) {
- if (orientation != DISPLAY_ORIENTATION_0) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][orientation];
+static int32_t rotateKeyCode(int32_t keyCode, ui::Rotation orientation) {
+ static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
+ // key codes enumerated counter-clockwise with the original (unrotated) key first
+ // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
+ {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
+ {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
+ {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
+ {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
+ {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+ AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
+ {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+ AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
+ {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+ AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
+ {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+ AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
+ };
+
+ if (orientation != ui::ROTATION_0) {
+ for (const auto& rotation : KEYCODE_ROTATION_MAP) {
+ if (rotation[static_cast<size_t>(ui::ROTATION_0)] == keyCode) {
+ return rotation[static_cast<size_t>(orientation)];
}
}
}
- return value;
+ return keyCode;
}
-static const int32_t keyCodeRotationMap[][4] = {
- // key codes enumerated counter-clockwise with the original (unrotated) key first
- // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
- {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
- {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN},
- {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT},
- {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP},
- {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
- AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT},
- {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
- AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN},
- {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
- AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT},
- {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
- AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
-};
+static bool isSupportedScanCode(int32_t scanCode) {
+ // KeyboardInputMapper handles keys from keyboards, gamepads, and styluses.
+ return scanCode < BTN_MOUSE || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI) ||
+ scanCode == BTN_STYLUS || scanCode == BTN_STYLUS2 || scanCode == BTN_STYLUS3 ||
+ scanCode >= BTN_WHEEL;
+}
-static const size_t keyCodeRotationMapSize =
- sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
-
-static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2],
- size_t mapSize) {
- if (orientation == DISPLAY_ORIENTATION_180) {
- for (size_t i = 0; i < mapSize; i++) {
- if (value == map[i][0]) {
- return map[i][1];
- }
- }
+static bool isMediaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MEDIA_PLAY:
+ case AKEYCODE_MEDIA_PAUSE:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_RECORD:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_BACKWARD:
+ case AKEYCODE_MEDIA_STEP_FORWARD:
+ case AKEYCODE_MEDIA_STEP_BACKWARD:
+ case AKEYCODE_MEDIA_AUDIO_TRACK:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_VOLUME_MUTE:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+ return true;
+ default:
+ return false;
}
- return value;
-}
-
-// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
-static int32_t stemKeyRotationMap[][2] = {
- // key codes enumerated with the original (unrotated) key first
- // no rotation, 180 degree rotation
- {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY},
- {AKEYCODE_STEM_1, AKEYCODE_STEM_1},
- {AKEYCODE_STEM_2, AKEYCODE_STEM_2},
- {AKEYCODE_STEM_3, AKEYCODE_STEM_3},
-};
-
-static const size_t stemKeyRotationMapSize =
- sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
-
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
- keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
- return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
- keyCodeRotationMapSize);
}
// --- KeyboardInputMapper ---
@@ -93,17 +97,15 @@
int32_t keyboardType)
: InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {}
-KeyboardInputMapper::~KeyboardInputMapper() {}
-
uint32_t KeyboardInputMapper::getSources() const {
return mSource;
}
-int32_t KeyboardInputMapper::getOrientation() {
+ui::Rotation KeyboardInputMapper::getOrientation() {
if (mViewport) {
return mViewport->orientation;
}
- return DISPLAY_ORIENTATION_0;
+ return ui::ROTATION_0;
}
int32_t KeyboardInputMapper::getDisplayId() {
@@ -130,7 +132,7 @@
}
std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
- nsecs_t when, const InputReaderConfiguration* config) {
+ const InputReaderConfiguration* config) {
if (getDeviceContext().getAssociatedViewport()) {
return getDeviceContext().getAssociatedViewport();
}
@@ -143,9 +145,10 @@
return std::nullopt;
}
-void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> KeyboardInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
if (!changes) { // first time only
// Configure basic parameters.
@@ -153,20 +156,9 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mViewport = findViewport(when, config);
+ mViewport = findViewport(config);
}
-}
-
-static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
- int32_t mapped = 0;
- if (config.tryGetProperty(property, mapped) && mapped > 0) {
- for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
- if (stemKeyRotationMap[i][0] == keyCode) {
- stemKeyRotationMap[i][1] = mapped;
- return;
- }
- }
- }
+ return out;
}
void KeyboardInputMapper::configureParameters() {
@@ -174,13 +166,6 @@
const PropertyMap& config = getDeviceContext().getConfiguration();
config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);
- if (mParameters.orientationAware) {
- mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
- mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
- mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
- mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
- }
-
mParameters.handlesKeyRepeat = false;
config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);
@@ -188,85 +173,42 @@
config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
}
-void KeyboardInputMapper::dumpParameters(std::string& dump) {
+void KeyboardInputMapper::dumpParameters(std::string& dump) const {
dump += INDENT3 "Parameters:\n";
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
}
-void KeyboardInputMapper::reset(nsecs_t when) {
- cancelAllDownKeys(when);
- mCurrentHidUsage = 0;
+std::list<NotifyArgs> KeyboardInputMapper::reset(nsecs_t when) {
+ std::list<NotifyArgs> out = cancelAllDownKeys(when);
+ mHidUsageAccumulator.reset();
resetLedState();
- InputMapper::reset(when);
+ out += InputMapper::reset(when);
+ return out;
}
-void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
+ mHidUsageAccumulator.process(*rawEvent);
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
- int32_t usageCode = mCurrentHidUsage;
- mCurrentHidUsage = 0;
- if (isKeyboardOrGamepadKey(scanCode)) {
- processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
- usageCode);
+ if (isSupportedScanCode(scanCode)) {
+ out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
+ scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
}
break;
}
- case EV_MSC: {
- if (rawEvent->code == MSC_SCAN) {
- mCurrentHidUsage = rawEvent->value;
- }
- break;
- }
- case EV_SYN: {
- if (rawEvent->code == SYN_REPORT) {
- mCurrentHidUsage = 0;
- }
- }
}
+ return out;
}
-bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
- return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
- (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
- (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
-}
-
-bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
- switch (keyCode) {
- case AKEYCODE_MEDIA_PLAY:
- case AKEYCODE_MEDIA_PAUSE:
- case AKEYCODE_MEDIA_PLAY_PAUSE:
- case AKEYCODE_MUTE:
- case AKEYCODE_HEADSETHOOK:
- case AKEYCODE_MEDIA_STOP:
- case AKEYCODE_MEDIA_NEXT:
- case AKEYCODE_MEDIA_PREVIOUS:
- case AKEYCODE_MEDIA_REWIND:
- case AKEYCODE_MEDIA_RECORD:
- case AKEYCODE_MEDIA_FAST_FORWARD:
- case AKEYCODE_MEDIA_SKIP_FORWARD:
- case AKEYCODE_MEDIA_SKIP_BACKWARD:
- case AKEYCODE_MEDIA_STEP_FORWARD:
- case AKEYCODE_MEDIA_STEP_BACKWARD:
- case AKEYCODE_MEDIA_AUDIO_TRACK:
- case AKEYCODE_VOLUME_UP:
- case AKEYCODE_VOLUME_DOWN:
- case AKEYCODE_VOLUME_MUTE:
- case AKEYCODE_TV_AUDIO_DESCRIPTION:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
- case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
- return true;
- }
- return false;
-}
-
-void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
- int32_t usageCode) {
+std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
+ int32_t scanCode, int32_t usageCode) {
+ std::list<NotifyArgs> out;
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
@@ -279,6 +221,7 @@
}
nsecs_t downTime = when;
+ std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode);
if (down) {
// Rotate key codes according to orientation if needed.
if (mParameters.orientationAware) {
@@ -286,19 +229,18 @@
}
// Add key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
+ if (keyDownIndex) {
// key repeat, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns[keyDownIndex].keyCode;
- downTime = mKeyDowns[keyDownIndex].downTime;
+ keyCode = mKeyDowns[*keyDownIndex].keyCode;
+ downTime = mKeyDowns[*keyDownIndex].downTime;
} else {
// key down
if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) {
- return;
+ return out;
}
if (policyFlags & POLICY_FLAG_GESTURE) {
- getDeviceContext().cancelTouch(when, readTime);
+ out += getDeviceContext().cancelTouch(when, readTime);
}
KeyDown keyDown;
@@ -309,18 +251,17 @@
}
} else {
// Remove key down.
- ssize_t keyDownIndex = findKeyDown(scanCode);
- if (keyDownIndex >= 0) {
+ if (keyDownIndex) {
// key up, be sure to use same keycode as before in case of rotation
- keyCode = mKeyDowns[keyDownIndex].keyCode;
- downTime = mKeyDowns[keyDownIndex].downTime;
- mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
+ keyCode = mKeyDowns[*keyDownIndex].keyCode;
+ downTime = mKeyDowns[*keyDownIndex].downTime;
+ mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
} else {
// key was not actually down
ALOGI("Dropping key up from device %s because the key was not down. "
"keyCode=%d, scanCode=%d",
getDeviceName().c_str(), keyCode, scanCode);
- return;
+ return out;
}
}
@@ -347,21 +288,22 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- getDisplayId(), policyFlags,
- down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
- getListener().notifyKey(&args);
+ out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, getDisplayId(), policyFlags,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
+ downTime));
+ return out;
}
-ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+std::optional<size_t> KeyboardInputMapper::findKeyDownIndex(int32_t scanCode) {
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
if (mKeyDowns[i].scanCode == scanCode) {
return i;
}
}
- return -1;
+ return {};
}
int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -470,19 +412,20 @@
return std::nullopt;
}
-void KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
+std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
+ std::list<NotifyArgs> out;
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
- NotifyKeyArgs args(getContext()->getNextId(), when, systemTime(SYSTEM_TIME_MONOTONIC),
- getDeviceId(), mSource, getDisplayId(), 0 /*policyFlags*/,
- AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
- mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
- mKeyDowns[i].downTime);
- getListener().notifyKey(&args);
+ out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
+ systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
+ getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+ mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+ mKeyDowns[i].downTime));
}
mKeyDowns.clear();
mMetaState = AMETA_NONE;
+ return out;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 2136d25..0526fd8 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -16,6 +16,7 @@
#pragma once
+#include "HidUsageAccumulator.h"
#include "InputMapper.h"
namespace android {
@@ -23,81 +24,79 @@
class KeyboardInputMapper : public InputMapper {
public:
KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
- virtual ~KeyboardInputMapper();
+ ~KeyboardInputMapper() override = default;
- virtual uint32_t getSources() const override;
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
- virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ uint32_t getSources() const override;
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void dump(std::string& dump) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
- virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
- virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
- virtual bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
- uint8_t* outFlags) override;
- virtual int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
+ int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+ int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
+ bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
+ uint8_t* outFlags) override;
+ int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
- virtual int32_t getMetaState() override;
- virtual bool updateMetaState(int32_t keyCode) override;
- virtual std::optional<int32_t> getAssociatedDisplayId() override;
- virtual void updateLedState(bool reset);
+ int32_t getMetaState() override;
+ bool updateMetaState(int32_t keyCode) override;
+ std::optional<int32_t> getAssociatedDisplayId() override;
+ void updateLedState(bool reset) override;
private:
// The current viewport.
- std::optional<DisplayViewport> mViewport;
+ std::optional<DisplayViewport> mViewport{};
struct KeyDown {
- nsecs_t downTime;
- int32_t keyCode;
- int32_t scanCode;
+ nsecs_t downTime{};
+ int32_t keyCode{};
+ int32_t scanCode{};
};
- uint32_t mSource;
- int32_t mKeyboardType;
+ uint32_t mSource{};
+ int32_t mKeyboardType{};
- std::vector<KeyDown> mKeyDowns; // keys that are down
- int32_t mMetaState;
+ std::vector<KeyDown> mKeyDowns{}; // keys that are down
+ int32_t mMetaState{};
- int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
+ HidUsageAccumulator mHidUsageAccumulator;
struct LedState {
- bool avail; // led is available
- bool on; // we think the led is currently on
+ bool avail{}; // led is available
+ bool on{}; // we think the led is currently on
};
- LedState mCapsLockLedState;
- LedState mNumLockLedState;
- LedState mScrollLockLedState;
+ LedState mCapsLockLedState{};
+ LedState mNumLockLedState{};
+ LedState mScrollLockLedState{};
// Immutable configuration parameters.
struct Parameters {
- bool orientationAware;
- bool handlesKeyRepeat;
- bool doNotWakeByDefault;
- } mParameters;
+ bool orientationAware{};
+ bool handlesKeyRepeat{};
+ bool doNotWakeByDefault{};
+ } mParameters{};
void configureParameters();
- void dumpParameters(std::string& dump);
+ void dumpParameters(std::string& dump) const;
- int32_t getOrientation();
+ ui::Rotation getOrientation();
int32_t getDisplayId();
- bool isKeyboardOrGamepadKey(int32_t scanCode);
- bool isMediaKey(int32_t keyCode);
-
- void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode);
+ [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
+ int32_t scanCode, int32_t usageCode);
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
- ssize_t findKeyDown(int32_t scanCode);
+ std::optional<size_t> findKeyDownIndex(int32_t scanCode);
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
- std::optional<DisplayViewport> findViewport(nsecs_t when,
- const InputReaderConfiguration* config);
- void cancelAllDownKeys(nsecs_t when);
+ std::optional<DisplayViewport> findViewport(const InputReaderConfiguration* config);
+ [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 8f5dc9b..633efc6 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -16,9 +16,8 @@
#include "../Macros.h"
-#include "MultiTouchInputMapper.h"
-
#include <android/sysprop/InputProperties.sysprop.h>
+#include "MultiTouchInputMapper.h"
namespace android {
@@ -27,169 +26,6 @@
// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
static constexpr size_t MAX_SLOTS = 32;
-// --- MultiTouchMotionAccumulator ---
-
-MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
- : mCurrentSlot(-1),
- mUsingSlotsProtocol(false),
- mHaveStylus(false) {}
-
-void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
- bool usingSlotsProtocol) {
- mUsingSlotsProtocol = usingSlotsProtocol;
- mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
-
- mSlots = std::vector<Slot>(slotCount);
-}
-
-void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
- // Unfortunately there is no way to read the initial contents of the slots.
- // So when we reset the accumulator, we must assume they are all zeroes.
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot
- // before we start reading events from the device. It is possible that the
- // current slot index will not be the same as it was when the first event was
- // written into the evdev buffer, which means the input mapper could start
- // out of sync with the initial state of the events in the evdev buffer.
- // In the extremely unlikely case that this happens, the data from
- // two slots will be confused until the next ABS_MT_SLOT event is received.
- // This can cause the touch point to "jump", but at least there will be
- // no stuck touches.
- int32_t initialSlot;
- status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- if (status) {
- ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
- initialSlot = -1;
- }
- clearSlots(initialSlot);
- } else {
- clearSlots(-1);
- }
-}
-
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
- for (Slot& slot : mSlots) {
- slot.clear();
- }
- mCurrentSlot = initialSlot;
-}
-
-void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
- bool newSlot = false;
- if (mUsingSlotsProtocol) {
- if (rawEvent->code == ABS_MT_SLOT) {
- mCurrentSlot = rawEvent->value;
- newSlot = true;
- }
- } else if (mCurrentSlot < 0) {
- mCurrentSlot = 0;
- }
-
- if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
- if (DEBUG_POINTERS) {
- if (newSlot) {
- ALOGW("MultiTouch device emitted invalid slot index %d but it "
- "should be between 0 and %zd; ignoring this slot.",
- mCurrentSlot, mSlots.size() - 1);
- }
- }
- } else {
- Slot& slot = mSlots[mCurrentSlot];
- // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
- // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
- // updating the slot.
- if (!mUsingSlotsProtocol) {
- slot.mInUse = true;
- }
-
- switch (rawEvent->code) {
- case ABS_MT_POSITION_X:
- slot.mAbsMTPositionX = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_POSITION_Y:
- slot.mAbsMTPositionY = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_TOUCH_MAJOR:
- slot.mAbsMTTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- slot.mAbsMTTouchMinor = rawEvent->value;
- slot.mHaveAbsMTTouchMinor = true;
- break;
- case ABS_MT_WIDTH_MAJOR:
- slot.mAbsMTWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- slot.mAbsMTWidthMinor = rawEvent->value;
- slot.mHaveAbsMTWidthMinor = true;
- break;
- case ABS_MT_ORIENTATION:
- slot.mAbsMTOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (mUsingSlotsProtocol && rawEvent->value < 0) {
- // The slot is no longer in use but it retains its previous contents,
- // which may be reused for subsequent touches.
- slot.mInUse = false;
- } else {
- slot.mInUse = true;
- slot.mAbsMTTrackingId = rawEvent->value;
- }
- break;
- case ABS_MT_PRESSURE:
- slot.mAbsMTPressure = rawEvent->value;
- break;
- case ABS_MT_DISTANCE:
- slot.mAbsMTDistance = rawEvent->value;
- break;
- case ABS_MT_TOOL_TYPE:
- slot.mAbsMTToolType = rawEvent->value;
- slot.mHaveAbsMTToolType = true;
- break;
- }
- }
- } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
- // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
- mCurrentSlot += 1;
- }
-}
-
-void MultiTouchMotionAccumulator::finishSync() {
- if (!mUsingSlotsProtocol) {
- clearSlots(-1);
- }
-}
-
-bool MultiTouchMotionAccumulator::hasStylus() const {
- return mHaveStylus;
-}
-
-void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
- if (!slot.mInUse) {
- ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
- event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
- }
-}
-
-// --- MultiTouchMotionAccumulator::Slot ---
-
-int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
- if (mHaveAbsMTToolType) {
- switch (mAbsMTToolType) {
- case MT_TOOL_FINGER:
- return AMOTION_EVENT_TOOL_TYPE_FINGER;
- case MT_TOOL_PEN:
- return AMOTION_EVENT_TOOL_TYPE_STYLUS;
- case MT_TOOL_PALM:
- return AMOTION_EVENT_TOOL_TYPE_PALM;
- }
- }
- return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-}
-
// --- MultiTouchInputMapper ---
MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
@@ -197,18 +33,21 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
-void MultiTouchInputMapper::reset(nsecs_t when) {
- mMultiTouchMotionAccumulator.reset(getDeviceContext());
-
- mPointerIdBits.clear();
-
- TouchInputMapper::reset(when);
+std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
+ // The evdev multi-touch protocol does not allow userspace applications to query the initial or
+ // current state of the pointers at any time. This means if we clear our accumulated state when
+ // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
+ // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
+ // rebuilding the state from scratch, we work around this kernel API limitation by never
+ // fully clearing any state specific to the multi-touch protocol.
+ return TouchInputMapper::reset(when);
}
-void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
+std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
mMultiTouchMotionAccumulator.process(rawEvent);
+ return out;
}
std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
@@ -278,6 +117,18 @@
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
}
+ } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) {
+ mStylusMtToolSeen = true;
+ // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
+ // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
+ // This is to cover the case where we cannot reliably detect whether a multi-touch
+ // device will ever produce stylus events when it is initially being configured.
+ if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
+ // Add the stylus source immediately so that it is included in any events generated
+ // before we have a chance to re-configure the device.
+ mSource |= AINPUT_SOURCE_STYLUS;
+ bumpGeneration();
+ }
}
if (shouldSimulateStylusWithTouch() &&
outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
@@ -359,7 +210,7 @@
}
bool MultiTouchInputMapper::hasStylus() const {
- return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
+ return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
shouldSimulateStylusWithTouch();
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 212c9c9..5f8bccf 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -17,85 +17,17 @@
#pragma once
#include "TouchInputMapper.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
namespace android {
-/* Keeps track of the state of multi-touch protocol. */
-class MultiTouchMotionAccumulator {
-public:
- class Slot {
- public:
- inline bool isInUse() const { return mInUse; }
- inline int32_t getX() const { return mAbsMTPositionX; }
- inline int32_t getY() const { return mAbsMTPositionY; }
- inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; }
- inline int32_t getTouchMinor() const {
- return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor;
- }
- inline int32_t getToolMajor() const { return mAbsMTWidthMajor; }
- inline int32_t getToolMinor() const {
- return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor;
- }
- inline int32_t getOrientation() const { return mAbsMTOrientation; }
- inline int32_t getTrackingId() const { return mAbsMTTrackingId; }
- inline int32_t getPressure() const { return mAbsMTPressure; }
- inline int32_t getDistance() const { return mAbsMTDistance; }
- inline int32_t getToolType() const;
-
- private:
- friend class MultiTouchMotionAccumulator;
-
- bool mInUse = false;
- bool mHaveAbsMTTouchMinor = false;
- bool mHaveAbsMTWidthMinor = false;
- bool mHaveAbsMTToolType = false;
-
- int32_t mAbsMTPositionX = 0;
- int32_t mAbsMTPositionY = 0;
- int32_t mAbsMTTouchMajor = 0;
- int32_t mAbsMTTouchMinor = 0;
- int32_t mAbsMTWidthMajor = 0;
- int32_t mAbsMTWidthMinor = 0;
- int32_t mAbsMTOrientation = 0;
- int32_t mAbsMTTrackingId = -1;
- int32_t mAbsMTPressure = 0;
- int32_t mAbsMTDistance = 0;
- int32_t mAbsMTToolType = 0;
-
- void clear() { *this = Slot(); }
- };
-
- MultiTouchMotionAccumulator();
-
- void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
- void reset(InputDeviceContext& deviceContext);
- void process(const RawEvent* rawEvent);
- void finishSync();
- bool hasStylus() 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);
- return mSlots[index];
- }
-
-private:
- int32_t mCurrentSlot;
- std::vector<Slot> mSlots;
- bool mUsingSlotsProtocol;
- bool mHaveStylus;
-
- void clearSlots(int32_t initialSlot);
- void warnIfNotInUse(const RawEvent& event, const Slot& slot);
-};
-
class MultiTouchInputMapper : public TouchInputMapper {
public:
explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
~MultiTouchInputMapper() override;
- void reset(nsecs_t when) override;
- void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
protected:
void syncTouch(nsecs_t when, RawState* outState) override;
@@ -118,6 +50,8 @@
// Specifies the pointer id bits that are in use, and their associated tracking id.
BitSet32 mPointerIdBits;
int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+
+ bool mStylusMtToolSeen{false};
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 05973f7..19a79d7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -25,7 +25,7 @@
namespace android {
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) {
+ : InputMapper(deviceContext), mOrientation(ui::ROTATION_0) {
mSource = AINPUT_SOURCE_ROTARY_ENCODER;
}
@@ -60,9 +60,10 @@
toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel()));
}
-void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> RotaryEncoderInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
if (!changes) {
mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
}
@@ -72,65 +73,69 @@
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
- mOrientation = DISPLAY_ORIENTATION_0;
+ mOrientation = ui::ROTATION_0;
}
}
+ return out;
}
-void RotaryEncoderInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::reset(nsecs_t when) {
mRotaryEncoderScrollAccumulator.reset(getDeviceContext());
- InputMapper::reset(when);
+ return InputMapper::reset(when);
}
-void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
mRotaryEncoderScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when, rawEvent->readTime);
+ out += sync(rawEvent->when, rawEvent->readTime);
}
+ return out;
}
-void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
bool scrolled = scroll != 0;
- // This is not a pointer, so it's not associated with a display.
- int32_t displayId = ADISPLAY_ID_NONE;
-
- // Moving the rotary encoder should wake the device (if specified).
- uint32_t policyFlags = 0;
- if (scrolled && getDeviceContext().isExternal()) {
- policyFlags |= POLICY_FLAG_WAKE;
- }
-
- if (mOrientation == DISPLAY_ORIENTATION_180) {
- scroll = -scroll;
- }
-
// Send motion event.
if (scrolled) {
int32_t metaState = getContext()->getGlobalMetaState();
+ // This is not a pointer, so it's not associated with a display.
+ int32_t displayId = ADISPLAY_ID_NONE;
+
+ if (mOrientation == ui::ROTATION_180) {
+ scroll = -scroll;
+ }
+
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0,
- 0, metaState, /* buttonState */ 0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener().notifyMotion(&scrollArgs);
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+ uint32_t policyFlags = 0;
+ if (getDeviceContext().isExternal()) {
+ policyFlags |= POLICY_FLAG_WAKE;
+ }
+
+ out.push_back(
+ NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+ metaState, /* buttonState */ 0, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}));
}
mRotaryEncoderScrollAccumulator.finishSync();
+ return out;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 42e2421..cb5fd88 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ui/Rotation.h>
+
#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
@@ -29,19 +31,20 @@
virtual uint32_t getSources() const override;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
virtual void dump(std::string& dump) override;
- virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) override;
- virtual void reset(nsecs_t when) override;
- virtual void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
private:
CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
int32_t mSource;
float mScalingFactor;
- int32_t mOrientation;
+ ui::Rotation mOrientation;
- void sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 573f99c..d81022f 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -122,9 +122,10 @@
}
}
-void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> SensorInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
if (!changes) { // first time only
mDeviceEnabled = true;
@@ -158,6 +159,7 @@
}
}
}
+ return out;
}
SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo,
@@ -185,7 +187,7 @@
return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter);
}
-void SensorInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> SensorInputMapper::reset(nsecs_t when) {
// Recenter all axes.
for (std::pair<const int32_t, Axis>& pair : mAxes) {
Axis& axis = pair.second;
@@ -193,7 +195,7 @@
}
mHardwareTimestamp = 0;
mPrevMscTime = 0;
- InputMapper::reset(when);
+ return InputMapper::reset(when);
}
SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
@@ -256,7 +258,8 @@
mPrevMscTime = static_cast<uint32_t>(mscTime);
}
-void SensorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
switch (rawEvent->type) {
case EV_ABS: {
auto it = mAxes.find(rawEvent->code);
@@ -274,7 +277,7 @@
Axis& axis = pair.second;
axis.currentValue = axis.newValue;
}
- sync(rawEvent->when, false /*force*/);
+ out += sync(rawEvent->when, false /*force*/);
break;
}
break;
@@ -287,6 +290,7 @@
break;
}
}
+ return out;
}
bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) {
@@ -375,7 +379,8 @@
}
}
-void SensorInputMapper::sync(nsecs_t when, bool force) {
+std::list<NotifyArgs> SensorInputMapper::sync(nsecs_t when, bool force) {
+ std::list<NotifyArgs> out;
for (auto& [sensorType, sensor] : mSensors) {
// Skip if sensor not enabled
if (!sensor.enabled) {
@@ -405,17 +410,17 @@
// Convert to Android unit
convertFromLinuxToAndroid(values, sensorType);
// Notify dispatcher for sensor event
- NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
- AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
- sensor.accuracy !=
- sensor.sensorInfo.accuracy /* accuracyChanged */,
- timestamp /* hwTimestamp */, values);
-
- getListener().notifySensor(&args);
+ out.push_back(NotifySensorArgs(getContext()->getNextId(), when, getDeviceId(),
+ AINPUT_SOURCE_SENSOR, sensorType,
+ sensor.sensorInfo.accuracy,
+ sensor.accuracy !=
+ sensor.sensorInfo.accuracy /* accuracyChanged */,
+ timestamp /* hwTimestamp */, values));
sensor.lastSampleTimeNs = timestamp;
sensor.accuracy = sensor.sensorInfo.accuracy;
}
}
+ return out;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 38d4c3c..457567b 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -30,9 +30,11 @@
uint32_t getSources() const override;
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
void dump(std::string& dump) override;
- void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
- void reset(nsecs_t when) override;
- void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency) override;
void disableSensor(InputDeviceSensorType sensorType) override;
@@ -116,7 +118,7 @@
// Sensor list
std::unordered_map<InputDeviceSensorType, Sensor> mSensors;
- void sync(nsecs_t when, bool force);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, bool force);
template <typename T>
bool tryGetProperty(std::string keyName, T& outValue);
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 4fff9be..13ad224 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -23,16 +23,17 @@
SingleTouchInputMapper::~SingleTouchInputMapper() {}
-void SingleTouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> SingleTouchInputMapper::reset(nsecs_t when) {
mSingleTouchMotionAccumulator.reset(getDeviceContext());
- TouchInputMapper::reset(when);
+ return TouchInputMapper::reset(when);
}
-void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
- TouchInputMapper::process(rawEvent);
+std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
mSingleTouchMotionAccumulator.process(rawEvent);
+ return out;
}
void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index f54c195..662e6bc 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -26,8 +26,8 @@
explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
~SingleTouchInputMapper() override;
- void reset(nsecs_t when) override;
- void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
protected:
void syncTouch(nsecs_t when, RawState* outState) override;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index ebb5de6..c9101ca 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -29,7 +29,8 @@
return AINPUT_SOURCE_SWITCH;
}
-void SwitchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out;
switch (rawEvent->type) {
case EV_SW:
processSwitch(rawEvent->code, rawEvent->value);
@@ -37,9 +38,10 @@
case EV_SYN:
if (rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ out += sync(rawEvent->when);
}
}
+ return out;
}
void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
@@ -53,15 +55,16 @@
}
}
-void SwitchInputMapper::sync(nsecs_t when) {
+std::list<NotifyArgs> SwitchInputMapper::sync(nsecs_t when) {
+ std::list<NotifyArgs> out;
if (mUpdatedSwitchMask) {
uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
- NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
- updatedSwitchValues, mUpdatedSwitchMask);
- getListener().notifySwitch(&args);
+ out.push_back(NotifySwitchArgs(getContext()->getNextId(), when, 0 /*policyFlags*/,
+ updatedSwitchValues, mUpdatedSwitchMask));
mUpdatedSwitchMask = 0;
}
+ return out;
}
int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index e0c949f..06d6504 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -26,7 +26,7 @@
virtual ~SwitchInputMapper();
virtual uint32_t getSources() const override;
- virtual void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
virtual void dump(std::string& dump) override;
@@ -36,7 +36,7 @@
uint32_t mUpdatedSwitchMask;
void processSwitch(int32_t switchCode, int32_t switchValue);
- void sync(nsecs_t when);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
new file mode 100644
index 0000000..c12e95d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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 <input/DisplayViewport.h>
+#include <stdint.h>
+#include <ui/Rotation.h>
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+namespace {
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+ std::list<NotifyArgs> out;
+ if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
+ (currentButtonState & buttonState)) ||
+ (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
+ !(currentButtonState & buttonState))) {
+ out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+ displayId, policyFlags, action, 0, keyCode, 0,
+ context->getGlobalMetaState(), when));
+ }
+ return out;
+}
+
+} // namespace
+
+ui::Rotation getInverseRotation(ui::Rotation orientation) {
+ switch (orientation) {
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
+ default:
+ return orientation;
+ }
+}
+
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) {
+ float temp;
+ switch (orientation) {
+ case ui::ROTATION_90:
+ temp = *deltaX;
+ *deltaX = *deltaY;
+ *deltaY = -temp;
+ break;
+
+ case ui::ROTATION_180:
+ *deltaX = -*deltaX;
+ *deltaY = -*deltaY;
+ break;
+
+ case ui::ROTATION_270:
+ temp = *deltaX;
+ *deltaX = -*deltaY;
+ *deltaY = temp;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool isPointerDown(int32_t buttonState) {
+ return buttonState &
+ (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState) {
+ std::list<NotifyArgs> out;
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+ return out;
+}
+
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+ const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+ nsecs_t lastEventTime) {
+ if (identifier.bus != BUS_BLUETOOTH) {
+ return {currentEventTime, readTime};
+ }
+
+ // Assume the fastest rate at which a Bluetooth touch device can report input events is one
+ // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
+ // will be separated by at least this amount.
+ constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+ // We define a 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);
+ const nsecs_t smoothenedEventTime =
+ std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
+ currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+ // If we are modifying the event time, treat this event as a synthetically generated event for
+ // latency tracking purposes and use the event time as the read time (zero read latency).
+ const nsecs_t smoothenedReadTime =
+ smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
+ return {smoothenedEventTime, smoothenedReadTime};
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 42d819b..3023e68 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -18,6 +18,7 @@
#include <input/DisplayViewport.h>
#include <stdint.h>
+#include <ui/Rotation.h>
#include "EventHub.h"
#include "InputListener.h"
@@ -25,76 +26,30 @@
namespace android {
-// --- Static Definitions ---
+ui::Rotation getInverseRotation(ui::Rotation orientation);
-static int32_t getInverseRotation(int32_t orientation) {
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- return DISPLAY_ORIENTATION_270;
- case DISPLAY_ORIENTATION_270:
- return DISPLAY_ORIENTATION_90;
- default:
- return orientation;
- }
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
- float temp;
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- temp = *deltaX;
- *deltaX = *deltaY;
- *deltaY = -temp;
- break;
-
- case DISPLAY_ORIENTATION_180:
- *deltaX = -*deltaX;
- *deltaY = -*deltaY;
- break;
-
- case DISPLAY_ORIENTATION_270:
- temp = *deltaX;
- *deltaX = -*deltaY;
- *deltaY = temp;
- break;
-
- default:
- break;
- }
-}
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY);
// Returns true if the pointer should be reported as being down given the specified
// button states. This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
- return buttonState &
- (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY);
-}
+bool isPointerDown(int32_t buttonState);
-static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
- nsecs_t readTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
- int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
- if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
- (currentButtonState & buttonState)) ||
- (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
- !(currentButtonState & buttonState))) {
- NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
- policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
- context->getListener().notifyKey(&args);
- }
-}
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState);
-static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
- nsecs_t readTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
- int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
- lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
- AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
- lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
- AKEYCODE_FORWARD);
-}
+// For devices connected over Bluetooth, although they may produce events at a consistent rate,
+// the events might end up reaching Android in a "batched" manner through the Bluetooth
+// stack, where a few events may be clumped together and processed around the same time.
+// In this case, if the input device or its driver does not send or process the actual event
+// generation timestamps, the event time will set to whenever the kernel received the event.
+// When the timestamp deltas are minuscule for these batched events, any changes in x or y
+// coordinates result in extremely large instantaneous velocities, which can negatively impact
+// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps
+// differ by at least a minimum delta value.
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+ const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+ nsecs_t lastEventTime);
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 4cd2cce..cefc44e 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -21,23 +21,18 @@
#include "TouchInputMapper.h"
#include <ftl/enum.h>
+#include <input/PrintTools.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "TouchButtonAccumulator.h"
#include "TouchCursorInputMapperCommon.h"
+#include "ui/Rotation.h"
namespace android {
// --- Constants ---
-// Maximum amount of latency to add to touch events while waiting for data from an
-// external stylus.
-static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
-
-// Maximum amount of time to wait on touch data before pushing out new pressure data.
-static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
-
// Artificial latency on synthetic events created from stylus data without corresponding touch
// data.
static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10);
@@ -48,6 +43,19 @@
static const DisplayViewport kUninitializedViewport;
+static std::string toString(const Rect& rect) {
+ return base::StringPrintf("Rect{%d, %d, %d, %d}", rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static std::string toString(const ui::Size& size) {
+ return base::StringPrintf("%dx%d", size.width, size.height);
+}
+
+static bool isPointInRect(const Rect& rect, int32_t x, int32_t y) {
+ // Consider all four sides as "inclusive".
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
+}
+
template <typename T>
inline static void swap(T& a, T& b) {
T temp = a;
@@ -73,53 +81,33 @@
return value >= 8 ? value - 16 : value;
}
-// --- RawPointerAxes ---
+static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo(
+ const DisplayViewport& viewport, ui::Rotation naturalOrientation) {
+ ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight};
+ if (naturalOrientation == ui::ROTATION_90 || naturalOrientation == ui::ROTATION_270) {
+ std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height);
+ }
-RawPointerAxes::RawPointerAxes() {
- clear();
-}
+ ui::Transform rotate(ui::Transform::toRotationFlags(naturalOrientation),
+ rotatedDisplaySize.width, rotatedDisplaySize.height);
-void RawPointerAxes::clear() {
- x.clear();
- y.clear();
- pressure.clear();
- touchMajor.clear();
- touchMinor.clear();
- toolMajor.clear();
- toolMinor.clear();
- orientation.clear();
- distance.clear();
- tiltX.clear();
- tiltY.clear();
- trackingId.clear();
- slot.clear();
+ Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight,
+ viewport.physicalBottom};
+ physicalFrame = rotate.transform(physicalFrame);
+
+ LOG_ALWAYS_FATAL_IF(!physicalFrame.isValid());
+ if (physicalFrame.isEmpty()) {
+ ALOGE("Viewport is not set properly: %s", viewport.toString().c_str());
+ physicalFrame.right =
+ physicalFrame.left + (physicalFrame.width() == 0 ? 1 : physicalFrame.width());
+ physicalFrame.bottom =
+ physicalFrame.top + (physicalFrame.height() == 0 ? 1 : physicalFrame.height());
+ }
+ return {rotatedDisplaySize, physicalFrame};
}
// --- RawPointerData ---
-RawPointerData::RawPointerData() {
- clear();
-}
-
-void RawPointerData::clear() {
- pointerCount = 0;
- clearIdBits();
-}
-
-void RawPointerData::copyFrom(const RawPointerData& other) {
- pointerCount = other.pointerCount;
- hoveringIdBits = other.hoveringIdBits;
- touchingIdBits = other.touchingIdBits;
- canceledIdBits = other.canceledIdBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointers[i] = other.pointers[i];
-
- int id = pointers[i].id;
- idToIndex[id] = other.idToIndex[id];
- }
-}
-
void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
float x = 0, y = 0;
uint32_t count = touchingIdBits.count();
@@ -137,48 +125,14 @@
*outY = y;
}
-// --- CookedPointerData ---
-
-CookedPointerData::CookedPointerData() {
- clear();
-}
-
-void CookedPointerData::clear() {
- pointerCount = 0;
- hoveringIdBits.clear();
- touchingIdBits.clear();
- canceledIdBits.clear();
- validIdBits.clear();
-}
-
-void CookedPointerData::copyFrom(const CookedPointerData& other) {
- pointerCount = other.pointerCount;
- hoveringIdBits = other.hoveringIdBits;
- touchingIdBits = other.touchingIdBits;
- validIdBits = other.validIdBits;
-
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i].copyFrom(other.pointerProperties[i]);
- pointerCoords[i].copyFrom(other.pointerCoords[i]);
-
- int id = pointerProperties[i].id;
- idToIndex[id] = other.idToIndex[id];
- }
-}
-
// --- TouchInputMapper ---
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
: InputMapper(deviceContext),
+ mTouchButtonAccumulator(deviceContext),
mSource(0),
mDeviceMode(DeviceMode::DISABLED),
- mDisplayWidth(-1),
- mDisplayHeight(-1),
- mPhysicalWidth(-1),
- mPhysicalHeight(-1),
- mPhysicalLeft(0),
- mPhysicalTop(0),
- mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
+ mInputDeviceOrientation(ui::ROTATION_0) {}
TouchInputMapper::~TouchInputMapper() {}
@@ -189,73 +143,74 @@
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
InputMapper::populateDeviceInfo(info);
- if (mDeviceMode != DeviceMode::DISABLED) {
- info->addMotionRange(mOrientedRanges.x);
- info->addMotionRange(mOrientedRanges.y);
- info->addMotionRange(mOrientedRanges.pressure);
-
- if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
- // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
- //
- // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
- // motion, i.e. the hardware dimensions, as the finger could move completely across the
- // touchpad in one sample cycle.
- const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
- const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
- info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat,
- y.fuzz, y.resolution);
- }
-
- if (mOrientedRanges.size) {
- info->addMotionRange(*mOrientedRanges.size);
- }
-
- if (mOrientedRanges.touchMajor) {
- info->addMotionRange(*mOrientedRanges.touchMajor);
- info->addMotionRange(*mOrientedRanges.touchMinor);
- }
-
- if (mOrientedRanges.toolMajor) {
- info->addMotionRange(*mOrientedRanges.toolMajor);
- info->addMotionRange(*mOrientedRanges.toolMinor);
- }
-
- if (mOrientedRanges.orientation) {
- info->addMotionRange(*mOrientedRanges.orientation);
- }
-
- if (mOrientedRanges.distance) {
- info->addMotionRange(*mOrientedRanges.distance);
- }
-
- if (mOrientedRanges.tilt) {
- info->addMotionRange(*mOrientedRanges.tilt);
- }
-
- if (mCursorScrollAccumulator.haveRelativeVWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
- }
- if (mCursorScrollAccumulator.haveRelativeHWheel()) {
- info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- 0.0f);
- }
- if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
- const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
- const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat,
- y.fuzz, y.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat,
- x.fuzz, x.resolution);
- info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
- y.fuzz, y.resolution);
- }
- info->setButtonUnderPad(mParameters.hasButtonUnderPad);
+ if (mDeviceMode == DeviceMode::DISABLED) {
+ return;
}
+
+ info->addMotionRange(mOrientedRanges.x);
+ info->addMotionRange(mOrientedRanges.y);
+ info->addMotionRange(mOrientedRanges.pressure);
+
+ if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
+ // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
+ //
+ // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
+ // motion, i.e. the hardware dimensions, as the finger could move completely across the
+ // touchpad in one sample cycle.
+ const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+ const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz,
+ x.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz,
+ y.resolution);
+ }
+
+ if (mOrientedRanges.size) {
+ info->addMotionRange(*mOrientedRanges.size);
+ }
+
+ if (mOrientedRanges.touchMajor) {
+ info->addMotionRange(*mOrientedRanges.touchMajor);
+ info->addMotionRange(*mOrientedRanges.touchMinor);
+ }
+
+ if (mOrientedRanges.toolMajor) {
+ info->addMotionRange(*mOrientedRanges.toolMajor);
+ info->addMotionRange(*mOrientedRanges.toolMinor);
+ }
+
+ if (mOrientedRanges.orientation) {
+ info->addMotionRange(*mOrientedRanges.orientation);
+ }
+
+ if (mOrientedRanges.distance) {
+ info->addMotionRange(*mOrientedRanges.distance);
+ }
+
+ if (mOrientedRanges.tilt) {
+ info->addMotionRange(*mOrientedRanges.tilt);
+ }
+
+ if (mCursorScrollAccumulator.haveRelativeVWheel()) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+ }
+ if (mCursorScrollAccumulator.haveRelativeHWheel()) {
+ info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
+ }
+ if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {
+ const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
+ const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, x.fuzz,
+ x.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, y.fuzz,
+ y.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, x.fuzz,
+ x.resolution);
+ info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, y.fuzz,
+ y.resolution);
+ }
+ info->setButtonUnderPad(mParameters.hasButtonUnderPad);
+ info->setSupportsUsi(mParameters.supportsUsi);
}
void TouchInputMapper::dump(std::string& dump) {
@@ -330,9 +285,12 @@
dump += INDENT3 "Stylus Fusion:\n";
dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
toString(mExternalStylusConnected));
- dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId);
+ dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n",
+ toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
mExternalStylusFusionTimeout);
+ dump += StringPrintf(INDENT4 " External Stylus Buttons Applied: 0x%08x",
+ mExternalStylusButtonsApplied);
dump += INDENT3 "External Stylus State:\n";
dumpStylusState(dump, mExternalStylusState);
@@ -346,9 +304,10 @@
}
}
-void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
- uint32_t changes) {
- InputMapper::configure(when, config, changes);
+std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) {
+ std::list<NotifyArgs> out = InputMapper::configure(when, config, changes);
mConfig = *config;
@@ -358,7 +317,7 @@
// Configure common accumulators.
mCursorScrollAccumulator.configure(getDeviceContext());
- mTouchButtonAccumulator.configure(getDeviceContext());
+ mTouchButtonAccumulator.configure();
// Configure absolute axis information.
configureRawPointerAxes();
@@ -394,15 +353,13 @@
}
if (changes && resetNeeded) {
- // If the device needs to be reset, cancel any ongoing gestures and reset the state.
- cancelTouch(when, when);
- reset(when);
+ out += reset(when);
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
- NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
- getListener().notifyDeviceReset(&args);
+ out.emplace_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
+ return out;
}
void TouchInputMapper::resolveExternalStylusPresence() {
@@ -466,18 +423,18 @@
getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
- mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+ mParameters.orientation = ui::ROTATION_0;
std::string orientationString;
if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
orientationString)) {
if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
} else if (orientationString == "ORIENTATION_90") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+ mParameters.orientation = ui::ROTATION_90;
} else if (orientationString == "ORIENTATION_180") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+ mParameters.orientation = ui::ROTATION_180;
} else if (orientationString == "ORIENTATION_270") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+ mParameters.orientation = ui::ROTATION_270;
} else if (orientationString != "ORIENTATION_0") {
ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
}
@@ -506,6 +463,14 @@
// up in your pocket but you can enable it using the input device configuration.
mParameters.wake = getDeviceContext().isExternal();
getDeviceContext().getConfiguration().tryGetProperty("touch.wake", mParameters.wake);
+
+ mParameters.supportsUsi = false;
+ getDeviceContext().getConfiguration().tryGetProperty("touch.supportsUsi",
+ mParameters.supportsUsi);
+
+ mParameters.enableForInactiveViewport = false;
+ getDeviceContext().getConfiguration().tryGetProperty("touch.enableForInactiveViewport",
+ mParameters.enableForInactiveViewport);
}
void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -522,6 +487,9 @@
mParameters.uniqueDisplayId.c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n";
+ dump += StringPrintf(INDENT4 "SupportsUsi: %s\n", toString(mParameters.supportsUsi));
+ dump += StringPrintf(INDENT4 "EnableForInactiveViewport: %s\n",
+ toString(mParameters.enableForInactiveViewport));
}
void TouchInputMapper::configureRawPointerAxes() {
@@ -629,7 +597,7 @@
}
// Size of diagonal axis.
- const float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight);
+ const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height);
// Size factors.
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
@@ -712,8 +680,8 @@
void TouchInputMapper::initializeOrientedRanges() {
// Configure X and Y factors.
- mXScale = float(mDisplayWidth) / mRawPointerAxes.getRawWidth();
- mYScale = float(mDisplayHeight) / mRawPointerAxes.getRawHeight();
+ mXScale = float(mDisplayBounds.width) / mRawPointerAxes.getRawWidth();
+ mYScale = float(mDisplayBounds.height) / mRawPointerAxes.getRawHeight();
mXPrecision = 1.0f / mXScale;
mYPrecision = 1.0f / mYScale;
@@ -843,19 +811,19 @@
// Note that the maximum value reported is an inclusive maximum value so it is one
// unit less than the total width or height of the display.
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_90:
+ case ui::ROTATION_270:
mOrientedXPrecision = mYPrecision;
mOrientedYPrecision = mXPrecision;
mOrientedRanges.x.min = 0;
- mOrientedRanges.x.max = mDisplayHeight - 1;
+ mOrientedRanges.x.max = mDisplayBounds.height - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
mOrientedRanges.y.min = 0;
- mOrientedRanges.y.max = mDisplayWidth - 1;
+ mOrientedRanges.y.max = mDisplayBounds.width - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -866,13 +834,13 @@
mOrientedYPrecision = mYPrecision;
mOrientedRanges.x.min = 0;
- mOrientedRanges.x.max = mDisplayWidth - 1;
+ mOrientedRanges.x.max = mDisplayBounds.width - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
mOrientedRanges.y.min = 0;
- mOrientedRanges.y.max = mDisplayHeight - 1;
+ mOrientedRanges.y.max = mDisplayBounds.height - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -892,6 +860,8 @@
mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
+ } else {
+ mSource |= AINPUT_SOURCE_TOUCHPAD;
}
} else if (isTouchScreen()) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
@@ -924,15 +894,14 @@
"becomes available.",
getDeviceName().c_str());
mDeviceMode = DeviceMode::DISABLED;
- } else if (!newViewportOpt->isActive) {
+ } else if (!mParameters.enableForInactiveViewport && !newViewportOpt->isActive) {
ALOGI("Disabling %s (device %i) because the associated viewport is not active",
getDeviceName().c_str(), getDeviceId());
mDeviceMode = DeviceMode::DISABLED;
}
// Raw width and height in the natural orientation.
- const int32_t rawWidth = mRawPointerAxes.getRawWidth();
- const int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
const int32_t rawXResolution = mRawPointerAxes.x.resolution;
const int32_t rawYResolution = mRawPointerAxes.y.resolution;
// Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0.
@@ -948,67 +917,16 @@
mViewport = newViewport;
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
- // Convert rotated viewport to the natural orientation.
- int32_t naturalPhysicalWidth, naturalPhysicalHeight;
- int32_t naturalPhysicalLeft, naturalPhysicalTop;
- int32_t naturalDeviceWidth, naturalDeviceHeight;
+ const auto oldDisplayBounds = mDisplayBounds;
// Apply the inverse of the input device orientation so that the input device is
// configured in the same orientation as the viewport. The input device orientation will
// be re-applied by mInputDeviceOrientation.
- const int32_t naturalDeviceOrientation =
- (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
- switch (naturalDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalPhysicalTop = mViewport.physicalLeft;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_180:
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
- naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- case DISPLAY_ORIENTATION_270:
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.physicalTop;
- naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_0:
- default:
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.physicalLeft;
- naturalPhysicalTop = mViewport.physicalTop;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- }
+ const ui::Rotation naturalDeviceOrientation =
+ mViewport.orientation - mParameters.orientation;
- if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
- ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
- naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
- naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
- }
-
- mPhysicalWidth = naturalPhysicalWidth;
- mPhysicalHeight = naturalPhysicalHeight;
- mPhysicalLeft = naturalPhysicalLeft;
- mPhysicalTop = naturalPhysicalTop;
-
- const int32_t oldDisplayWidth = mDisplayWidth;
- const int32_t oldDisplayHeight = mDisplayHeight;
- mDisplayWidth = naturalDeviceWidth;
- mDisplayHeight = naturalDeviceHeight;
+ std::tie(mDisplayBounds, mPhysicalFrameInDisplay) =
+ getNaturalDisplayInfo(mViewport, naturalDeviceOrientation);
// 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
@@ -1016,26 +934,19 @@
// when the display rotation is applied later as a part of the per-window transform, we
// get the expected screen coordinates.
mInputDeviceOrientation = mParameters.orientationAware
- ? DISPLAY_ORIENTATION_0
+ ? ui::ROTATION_0
: getInverseRotation(mViewport.orientation);
// For orientation-aware devices that work in the un-rotated coordinate space, the
// viewport update should be skipped if it is only a change in the orientation.
skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware &&
- mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight &&
- viewportOrientationChanged;
+ mDisplayBounds == oldDisplayBounds && viewportOrientationChanged;
// Apply the input device orientation for the device.
- mInputDeviceOrientation =
- (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+ mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
} else {
- mPhysicalWidth = rawWidth;
- mPhysicalHeight = rawHeight;
- mPhysicalLeft = 0;
- mPhysicalTop = 0;
-
- mDisplayWidth = rawWidth;
- mDisplayHeight = rawHeight;
- mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
+ mDisplayBounds = rawSize;
+ mPhysicalFrameInDisplay = Rect{mDisplayBounds};
+ mInputDeviceOrientation = ui::ROTATION_0;
}
}
@@ -1066,9 +977,9 @@
}
if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
- ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
+ ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, "
"display id %d",
- getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight,
+ getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(),
mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
configureVirtualKeys();
@@ -1080,8 +991,8 @@
if (mDeviceMode == DeviceMode::POINTER) {
// Compute pointer gesture detection parameters.
- float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight);
+ float rawDiagonal = hypotf(rawSize.width, rawSize.height);
+ float displayDiagonal = hypotf(mDisplayBounds.width, mDisplayBounds.height);
// Scale movements such that one whole swipe of the touch pad covers a
// given area relative to the diagonal size of the display when no acceleration
@@ -1117,12 +1028,8 @@
void TouchInputMapper::dumpDisplay(std::string& dump) {
dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
- dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth);
- dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight);
- dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
- dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
- dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
- dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
+ dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str());
+ dump += StringPrintf(INDENT3 "PhysicalFrame: %s\n", toString(mPhysicalFrameInDisplay).c_str());
dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
}
@@ -1161,17 +1068,17 @@
int32_t halfWidth = virtualKeyDefinition.width / 2;
int32_t halfHeight = virtualKeyDefinition.height / 2;
- virtualKey.hitLeft =
- (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth +
+ virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth /
+ mDisplayBounds.width +
touchScreenLeft;
- virtualKey.hitRight =
- (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth +
+ virtualKey.hitRight = (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth /
+ mDisplayBounds.width +
touchScreenLeft;
- virtualKey.hitTop =
- (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight +
+ virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
+ mDisplayBounds.height +
touchScreenTop;
- virtualKey.hitBottom =
- (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight +
+ virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
+ mDisplayBounds.height +
touchScreenTop;
mVirtualKeys.push_back(virtualKey);
}
@@ -1438,10 +1345,13 @@
mInputDeviceOrientation);
}
-void TouchInputMapper::reset(nsecs_t when) {
+std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) {
+ std::list<NotifyArgs> out = cancelTouch(when, when);
+ updateTouchSpots();
+
mCursorButtonAccumulator.reset(getDeviceContext());
mCursorScrollAccumulator.reset(getDeviceContext());
- mTouchButtonAccumulator.reset(getDeviceContext());
+ mTouchButtonAccumulator.reset();
mPointerVelocityControl.reset();
mWheelXVelocityControl.reset();
@@ -1469,14 +1379,15 @@
mPointerController->clearSpots();
}
- InputMapper::reset(when);
+ return out += InputMapper::reset(when);
}
void TouchInputMapper::resetExternalStylus() {
mExternalStylusState.clear();
- mExternalStylusId = -1;
+ mFusedStylusPointerId.reset();
mExternalStylusFusionTimeout = LLONG_MAX;
mExternalStylusDataPending = false;
+ mExternalStylusButtonsApplied = 0;
}
void TouchInputMapper::clearStylusDataPendingFlags() {
@@ -1484,17 +1395,24 @@
mExternalStylusFusionTimeout = LLONG_MAX;
}
-void TouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
+ std::list<NotifyArgs> out;
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when, rawEvent->readTime);
+ out += sync(rawEvent->when, rawEvent->readTime);
}
+ return out;
}
-void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ if (mDeviceMode == DeviceMode::DISABLED) {
+ // Only save the last pending state when the device is disabled.
+ mRawStatesPending.clear();
+ }
// Push a new state.
mRawStatesPending.emplace_back();
@@ -1519,6 +1437,10 @@
const RawState& last =
mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1];
+ std::tie(next.when, next.readTime) =
+ applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(), when,
+ readTime, last.when);
+
// Assign pointer ids.
if (!mHavePointerIds) {
assignPointerIds(last, next);
@@ -1539,16 +1461,15 @@
next.rawPointerData.hoveringIdBits.value);
}
- processRawTouches(false /*timeout*/);
+ out += processRawTouches(false /*timeout*/);
+ return out;
}
-void TouchInputMapper::processRawTouches(bool timeout) {
+std::list<NotifyArgs> TouchInputMapper::processRawTouches(bool timeout) {
+ std::list<NotifyArgs> out;
if (mDeviceMode == DeviceMode::DISABLED) {
- // Drop all input if the device is disabled.
- cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
- mCurrentCookedState.clear();
- updateTouchSpots();
- return;
+ // Do not process raw event while the device is disabled.
+ return out;
}
// Drain any pending touch states. The invariant here is that the mCurrentRawState is always
@@ -1568,12 +1489,12 @@
// All ready to go.
clearStylusDataPendingFlags();
- mCurrentRawState.copyFrom(next);
+ mCurrentRawState = next;
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
mCurrentRawState.readTime = mLastRawState.readTime;
}
- cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
+ out += cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
}
if (count != 0) {
mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
@@ -1583,19 +1504,21 @@
if (timeout) {
nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
clearStylusDataPendingFlags();
- mCurrentRawState.copyFrom(mLastRawState);
+ mCurrentRawState = mLastRawState;
ALOGD_IF(DEBUG_STYLUS_FUSION,
"Timeout expired, synthesizing event with new stylus data");
const nsecs_t readTime = when; // consider this synthetic event to be zero latency
- cookAndDispatch(when, readTime);
+ out += cookAndDispatch(when, readTime);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
}
}
+ return out;
}
-void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
+std::list<NotifyArgs> TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
// Always start with a clean state.
mCurrentCookedState.clear();
@@ -1621,7 +1544,9 @@
// Consume raw off-screen touches before cooking pointer data.
// If touches are consumed, subsequent code will not receive any pointer data.
- if (consumeRawTouches(when, readTime, policyFlags)) {
+ bool consumed;
+ out += consumeRawTouches(when, readTime, policyFlags, consumed /*byref*/);
+ if (consumed) {
mCurrentRawState.rawPointerData.clear();
}
@@ -1634,9 +1559,9 @@
applyExternalStylusTouchState(when);
// Synthesize key down from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
- mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,
- mCurrentCookedState.buttonState);
+ out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+ mSource, mViewport.displayId, policyFlags,
+ mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
if (mDeviceMode == DeviceMode::POINTER) {
@@ -1644,8 +1569,7 @@
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ if (isStylusToolType(pointer.toolType)) {
mCurrentCookedState.stylusIdBits.markBit(id);
} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||
pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
@@ -1658,8 +1582,7 @@
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
- if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
- pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+ if (isStylusToolType(pointer.toolType)) {
mCurrentCookedState.stylusIdBits.markBit(id);
}
}
@@ -1678,15 +1601,15 @@
pointerUsage = PointerUsage::GESTURES;
}
- dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
+ out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
} else {
if (!mCurrentMotionAborted) {
updateTouchSpots();
- dispatchButtonRelease(when, readTime, policyFlags);
- dispatchHoverExit(when, readTime, policyFlags);
- dispatchTouches(when, readTime, policyFlags);
- dispatchHoverEnterAndMove(when, readTime, policyFlags);
- dispatchButtonPress(when, readTime, policyFlags);
+ out += dispatchButtonRelease(when, readTime, policyFlags);
+ out += dispatchHoverExit(when, readTime, policyFlags);
+ out += dispatchTouches(when, readTime, policyFlags);
+ out += dispatchHoverEnterAndMove(when, readTime, policyFlags);
+ out += dispatchButtonPress(when, readTime, policyFlags);
}
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
@@ -1695,17 +1618,18 @@
}
// Synthesize key up from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
- mViewport.displayId, policyFlags, mLastCookedState.buttonState,
- mCurrentCookedState.buttonState);
+ out += synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(),
+ mSource, mViewport.displayId, policyFlags,
+ mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Clear some transient state.
mCurrentRawState.rawVScroll = 0;
mCurrentRawState.rawHScroll = 0;
// Copy current touch to last touch in preparation for the next cycle.
- mLastRawState.copyFrom(mCurrentRawState);
- mLastCookedState.copyFrom(mCurrentCookedState);
+ mLastRawState = mCurrentRawState;
+ mLastCookedState = mCurrentCookedState;
+ return out;
}
void TouchInputMapper::updateTouchSpots() {
@@ -1724,8 +1648,8 @@
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
- mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
+ mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
+ mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
mCurrentCookedState.cookedPointerData.touchingIdBits,
mViewport.displayId);
}
@@ -1736,29 +1660,41 @@
}
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
- if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus() && mExternalStylusId != -1) {
- mCurrentRawState.buttonState |= mExternalStylusState.buttons;
+ if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) {
+ // If any of the external buttons are already pressed by the touch device, ignore them.
+ const int32_t pressedButtons = ~mCurrentRawState.buttonState & mExternalStylusState.buttons;
+ const int32_t releasedButtons =
+ mExternalStylusButtonsApplied & ~mExternalStylusState.buttons;
+
+ mCurrentRawState.buttonState |= pressedButtons;
+ mCurrentRawState.buttonState &= ~releasedButtons;
+
+ mExternalStylusButtonsApplied |= pressedButtons;
+ mExternalStylusButtonsApplied &= ~releasedButtons;
}
}
void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
+ if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) {
+ return;
+ }
- if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) {
- float pressure = mExternalStylusState.pressure;
- if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) {
- const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId);
- pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
- }
- PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId);
- coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ float pressure = lastPointerData.isTouching(*mFusedStylusPointerId)
+ ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId)
+ .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)
+ : 0.f;
+ if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) {
+ pressure = *mExternalStylusState.pressure;
+ }
+ PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
PointerProperties& properties =
- currentPointerData.editPointerPropertiesWithId(mExternalStylusId);
- if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
- properties.toolType = mExternalStylusState.toolType;
- }
+ currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId);
+ properties.toolType = mExternalStylusState.toolType;
}
}
@@ -1767,64 +1703,88 @@
return false;
}
+ // Check if the stylus pointer has gone up.
+ if (mFusedStylusPointerId &&
+ !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
+ mFusedStylusPointerId.reset();
+ return false;
+ }
+
const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
state.rawPointerData.pointerCount != 0;
- if (initialDown) {
- if (mExternalStylusState.pressure != 0.0f) {
- ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
- mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
- } else if (timeout) {
- ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
- resetExternalStylus();
- } else {
- if (mExternalStylusFusionTimeout == LLONG_MAX) {
- mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
- }
- ALOGD_IF(DEBUG_STYLUS_FUSION,
- "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
- mExternalStylusFusionTimeout);
- getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
- return true;
- }
+ if (!initialDown) {
+ return false;
}
- // Check if the stylus pointer has gone up.
- if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
- ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
- mExternalStylusId = -1;
+ if (!mExternalStylusState.pressure) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed");
+ return false;
}
- return false;
+ if (*mExternalStylusState.pressure != 0.0f) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
+ mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit();
+ return false;
+ }
+
+ if (timeout) {
+ ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
+ mFusedStylusPointerId.reset();
+ mExternalStylusFusionTimeout = LLONG_MAX;
+ return false;
+ }
+
+ // We are waiting for the external stylus to report a pressure value. Withhold touches from
+ // being processed until we either get pressure data or timeout.
+ if (mExternalStylusFusionTimeout == LLONG_MAX) {
+ mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
+ }
+ ALOGD_IF(DEBUG_STYLUS_FUSION,
+ "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
+ mExternalStylusFusionTimeout);
+ getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+ return true;
}
-void TouchInputMapper::timeoutExpired(nsecs_t when) {
+std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
+ std::list<NotifyArgs> out;
if (mDeviceMode == DeviceMode::POINTER) {
if (mPointerUsage == PointerUsage::GESTURES) {
// Since this is a synthetic event, we can consider its latency to be zero
const nsecs_t readTime = when;
- dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
+ out += dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
}
} else if (mDeviceMode == DeviceMode::DIRECT) {
- if (mExternalStylusFusionTimeout < when) {
- processRawTouches(true /*timeout*/);
+ if (mExternalStylusFusionTimeout <= when) {
+ out += processRawTouches(true /*timeout*/);
} else if (mExternalStylusFusionTimeout != LLONG_MAX) {
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
}
}
+ return out;
}
-void TouchInputMapper::updateExternalStylusState(const StylusState& state) {
- mExternalStylusState.copyFrom(state);
- if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) {
- // We're either in the middle of a fused stream of data or we're waiting on data before
- // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus
- // data.
+std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) {
+ std::list<NotifyArgs> out;
+ const bool buttonsChanged = mExternalStylusState.buttons != state.buttons;
+ mExternalStylusState = state;
+ if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) {
+ // The following three cases are handled here:
+ // - We're in the middle of a fused stream of data;
+ // - We're waiting on external stylus data before dispatching the initial down; or
+ // - Only the button state, which is not reported through a specific pointer, has changed.
+ // Go ahead and dispatch now that we have fresh stylus data.
mExternalStylusDataPending = true;
- processRawTouches(false /*timeout*/);
+ out += processRawTouches(false /*timeout*/);
}
+ return out;
}
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags, bool& outConsumed) {
+ outConsumed = false;
+ std::list<NotifyArgs> out;
// Check for release of a virtual key.
if (mCurrentVirtualKey.down) {
if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
@@ -1834,10 +1794,12 @@
ALOGD_IF(DEBUG_VIRTUAL_KEYS,
"VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
- dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+ out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM |
+ AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY));
}
- return true;
+ outConsumed = true;
+ return out;
}
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1847,7 +1809,8 @@
const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
// Pointer is still within the space of the virtual key.
- return true;
+ outConsumed = true;
+ return out;
}
}
@@ -1859,9 +1822,10 @@
if (!mCurrentVirtualKey.ignored) {
ALOGD_IF(DEBUG_VIRTUAL_KEYS, "VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
- dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
- AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
- AKEY_EVENT_FLAG_CANCELED);
+ out.push_back(dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM |
+ AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
+ AKEY_EVENT_FLAG_CANCELED));
}
}
@@ -1891,13 +1855,15 @@
ALOGD_IF(DEBUG_VIRTUAL_KEYS,
"VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
- dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
- AKEY_EVENT_FLAG_FROM_SYSTEM |
- AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+ out.push_back(dispatchVirtualKey(when, readTime, policyFlags,
+ AKEY_EVENT_ACTION_DOWN,
+ AKEY_EVENT_FLAG_FROM_SYSTEM |
+ AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY));
}
}
}
- return true;
+ outConsumed = true;
+ return out;
}
}
@@ -1919,44 +1885,80 @@
!mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
getContext()->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
}
- return false;
+ return out;
}
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags) {
+NotifyKeyArgs TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags, int32_t keyEventAction,
+ int32_t keyEventFlags) {
int32_t keyCode = mCurrentVirtualKey.keyCode;
int32_t scanCode = mCurrentVirtualKey.scanCode;
nsecs_t downTime = mCurrentVirtualKey.downTime;
int32_t metaState = getContext()->getGlobalMetaState();
policyFlags |= POLICY_FLAG_VIRTUAL;
- NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
- AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
- keyEventFlags, keyCode, scanCode, metaState, downTime);
- getListener().notifyKey(&args);
+ return NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
+ keyEventFlags, keyCode, scanCode, metaState, downTime);
}
-void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
if (mCurrentMotionAborted) {
// Current motion event was already aborted.
- return;
+ return out;
}
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
if (!currentIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
+ -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
mCurrentMotionAborted = true;
}
+ return out;
}
-void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+// Updates pointer coords and properties for pointers with specified ids that have moved.
+// Returns true if any of them changed.
+static bool updateMovedPointers(const PropertiesArray& inProperties, CoordsArray& inCoords,
+ const IdToIndexArray& inIdToIndex, PropertiesArray& outProperties,
+ CoordsArray& outCoords, IdToIndexArray& outIdToIndex,
+ BitSet32 idBits) {
+ bool changed = false;
+ while (!idBits.isEmpty()) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t inIndex = inIdToIndex[id];
+ uint32_t outIndex = outIdToIndex[id];
+
+ const PointerProperties& curInProperties = inProperties[inIndex];
+ const PointerCoords& curInCoords = inCoords[inIndex];
+ PointerProperties& curOutProperties = outProperties[outIndex];
+ PointerCoords& curOutCoords = outCoords[outIndex];
+
+ if (curInProperties != curOutProperties) {
+ curOutProperties.copyFrom(curInProperties);
+ changed = true;
+ }
+
+ if (curInCoords != curOutCoords) {
+ curOutCoords.copyFrom(curInCoords);
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+std::list<NotifyArgs> TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
int32_t metaState = getContext()->getGlobalMetaState();
@@ -1966,13 +1968,14 @@
if (!currentIdBits.isEmpty()) {
// No pointer id changes so this is a move event.
// The listener takes care of batching moves so we don't have to deal with that here.
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
+ -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
}
} else {
// There may be pointers going up and pointers going down and pointers moving
@@ -2002,13 +2005,16 @@
if (isCanceled) {
ALOGI("Canceling pointer %d for the palm event was detected.", upId);
}
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
- isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
- mLastCookedState.cookedPointerData.pointerProperties,
- mLastCookedState.cookedPointerData.pointerCoords,
- mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState,
+ buttonState, 0,
+ mLastCookedState.cookedPointerData.pointerProperties,
+ mLastCookedState.cookedPointerData.pointerCoords,
+ mLastCookedState.cookedPointerData.idToIndex,
+ dispatchedIdBits, upId, mOrientedXPrecision,
+ mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
dispatchedIdBits.clearBit(upId);
mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
}
@@ -2018,13 +2024,14 @@
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded && !moveIdBits.isEmpty()) {
ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
- metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ dispatchedIdBits, -1, mOrientedXPrecision,
+ mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
}
// Dispatch pointer down events using the new pointer locations.
@@ -2037,62 +2044,75 @@
mDownTime = when;
}
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
- 0, 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
- downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(
+ dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState,
+ 0, mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ dispatchedIdBits, downId, mOrientedXPrecision,
+ mOrientedYPrecision, mDownTime, MotionClassification::NONE));
}
}
+ return out;
}
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
if (mSentHoverEnter &&
(mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
!mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
int32_t metaState = getContext()->getGlobalMetaState();
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
- metaState, mLastCookedState.buttonState, 0,
- mLastCookedState.cookedPointerData.pointerProperties,
- mLastCookedState.cookedPointerData.pointerCoords,
- mLastCookedState.cookedPointerData.idToIndex,
- mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
- mOrientedYPrecision, mDownTime, MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+ mLastCookedState.buttonState, 0,
+ mLastCookedState.cookedPointerData.pointerProperties,
+ mLastCookedState.cookedPointerData.pointerCoords,
+ mLastCookedState.cookedPointerData.idToIndex,
+ mLastCookedState.cookedPointerData.hoveringIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
mSentHoverEnter = false;
}
+ return out;
}
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
- uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
!mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
if (!mSentHoverEnter) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
- 0, 0, metaState, mCurrentRawState.buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+ mCurrentRawState.buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
mSentHoverEnter = true;
}
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, mCurrentRawState.buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ mCurrentRawState.buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
}
+ return out;
}
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2100,17 +2120,21 @@
while (!releasedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
buttonState &= ~actionButton;
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- actionButton, 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+ metaState, buttonState, 0,
+ mLastCookedState.cookedPointerData.pointerProperties,
+ mLastCookedState.cookedPointerData.pointerCoords,
+ mLastCookedState.cookedPointerData.idToIndex, idBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
}
+ return out;
}
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2118,14 +2142,16 @@
while (!pressedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
buttonState |= actionButton;
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- actionButton, 0, metaState, buttonState, 0,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
- mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState,
+ buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
+ mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
}
+ return out;
}
const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) {
@@ -2321,7 +2347,7 @@
float left, top, right, bottom;
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
@@ -2332,7 +2358,7 @@
(mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
@@ -2343,7 +2369,7 @@
(mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
@@ -2407,54 +2433,62 @@
}
}
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- PointerUsage pointerUsage) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags,
+ PointerUsage pointerUsage) {
+ std::list<NotifyArgs> out;
if (pointerUsage != mPointerUsage) {
- abortPointerUsage(when, readTime, policyFlags);
+ out += abortPointerUsage(when, readTime, policyFlags);
mPointerUsage = pointerUsage;
}
switch (mPointerUsage) {
case PointerUsage::GESTURES:
- dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
+ out += dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
break;
case PointerUsage::STYLUS:
- dispatchPointerStylus(when, readTime, policyFlags);
+ out += dispatchPointerStylus(when, readTime, policyFlags);
break;
case PointerUsage::MOUSE:
- dispatchPointerMouse(when, readTime, policyFlags);
+ out += dispatchPointerMouse(when, readTime, policyFlags);
break;
case PointerUsage::NONE:
break;
}
+ return out;
}
-void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
switch (mPointerUsage) {
case PointerUsage::GESTURES:
- abortPointerGestures(when, readTime, policyFlags);
+ out += abortPointerGestures(when, readTime, policyFlags);
break;
case PointerUsage::STYLUS:
- abortPointerStylus(when, readTime, policyFlags);
+ out += abortPointerStylus(when, readTime, policyFlags);
break;
case PointerUsage::MOUSE:
- abortPointerMouse(when, readTime, policyFlags);
+ out += abortPointerMouse(when, readTime, policyFlags);
break;
case PointerUsage::NONE:
break;
}
mPointerUsage = PointerUsage::NONE;
+ return out;
}
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- bool isTimeout) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags,
+ bool isTimeout) {
+ std::list<NotifyArgs> out;
// Update current gesture coordinates.
bool cancelPreviousGesture, finishPreviousGesture;
bool sendEvents =
preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout);
if (!sendEvents) {
- return;
+ return {};
}
if (finishPreviousGesture) {
cancelPreviousGesture = false;
@@ -2468,8 +2502,8 @@
}
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- mPointerController->setSpots(mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
+ mPointerController->setSpots(mPointerGesture.currentGestureCoords.cbegin(),
+ mPointerGesture.currentGestureIdToIndex.cbegin(),
mPointerGesture.currentGestureIdBits,
mPointerController->getDisplayId());
}
@@ -2551,11 +2585,15 @@
BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0,
- flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
- mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
- mPointerGesture.downTime, classification);
+ const uint32_t cancelFlags = flags | AMOTION_EVENT_FLAG_CANCELED;
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_CANCEL, 0, cancelFlags, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureProperties,
+ mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex,
+ dispatchedGestureIdBits, -1, 0, 0,
+ mPointerGesture.downTime, classification));
dispatchedGestureIdBits.clear();
} else {
@@ -2569,12 +2607,14 @@
while (!upGestureIdBits.isEmpty()) {
uint32_t id = upGestureIdBits.clearFirstMarkedBit();
- dispatchMotion(when, readTime, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState,
- AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
- mPointerGesture.lastGestureCoords,
- mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
- 0, mPointerGesture.downTime, classification);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureProperties,
+ mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex,
+ dispatchedGestureIdBits, id, 0, 0,
+ mPointerGesture.downTime, classification));
dispatchedGestureIdBits.clearBit(id);
}
@@ -2583,12 +2623,13 @@
// Send motion events for all pointers that moved.
if (moveNeeded) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
- mPointerGesture.downTime, classification);
+ out.push_back(
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0,
+ flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1,
+ 0, 0, mPointerGesture.downTime, classification));
}
// Send motion events for all pointers that went down.
@@ -2603,24 +2644,26 @@
mPointerGesture.downTime = when;
}
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
- 0, flags, metaState, buttonState, 0,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
- 0, mPointerGesture.downTime, classification);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_POINTER_DOWN, 0, flags, metaState,
+ buttonState, 0, mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ dispatchedGestureIdBits, id, 0, 0,
+ mPointerGesture.downTime, classification));
}
}
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime,
- MotionClassification::NONE);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
+ buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits, -1, 0, 0,
+ mPointerGesture.downTime, MotionClassification::NONE));
} else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
// Synthesize a hover move event after all pointers go up to indicate that
// the pointer is hovering again even if the user is not currently touching
@@ -2640,12 +2683,13 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
const int32_t displayId = mPointerController->getDisplayId();
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags,
- metaState, buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
- 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
+ buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+ &pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
+ /* videoFrames */ {}));
}
// Update state.
@@ -2664,22 +2708,28 @@
mPointerGesture.lastGestureIdToIndex[id] = index;
}
}
+ return out;
}
-void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
const MotionClassification classification =
mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE
? MotionClassification::TWO_FINGER_SWIPE
: MotionClassification::NONE;
+ std::list<NotifyArgs> out;
// Cancel previously dispatches pointers.
if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentRawState.buttonState;
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
- mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime, classification);
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.lastGestureProperties,
+ mPointerGesture.lastGestureCoords,
+ mPointerGesture.lastGestureIdToIndex,
+ mPointerGesture.lastGestureIdBits, -1, 0, 0,
+ mPointerGesture.downTime, classification));
}
// Reset the current pointer gesture.
@@ -2691,6 +2741,7 @@
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->clearSpots();
}
+ return out;
}
bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
@@ -2757,54 +2808,20 @@
// Otherwise choose an arbitrary remaining pointer.
// This guarantees we always have an active touch id when there is at least one pointer.
// We keep the same active touch id for as long as possible.
- int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
- int32_t activeTouchId = lastActiveTouchId;
- if (activeTouchId < 0) {
+ if (mPointerGesture.activeTouchId < 0) {
if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentCookedState.fingerIdBits.firstMarkedBit();
+ mPointerGesture.activeTouchId = mCurrentCookedState.fingerIdBits.firstMarkedBit();
mPointerGesture.firstTouchTime = when;
}
- } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
- if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
- activeTouchId = mPointerGesture.activeTouchId =
- mCurrentCookedState.fingerIdBits.firstMarkedBit();
- } else {
- activeTouchId = mPointerGesture.activeTouchId = -1;
- }
+ } else if (!mCurrentCookedState.fingerIdBits.hasBit(mPointerGesture.activeTouchId)) {
+ mPointerGesture.activeTouchId = !mCurrentCookedState.fingerIdBits.isEmpty()
+ ? mCurrentCookedState.fingerIdBits.firstMarkedBit()
+ : -1;
}
-
- // Determine whether we are in quiet time.
- bool isQuietTime = false;
- if (activeTouchId < 0) {
- mPointerGesture.resetQuietTime();
- } else {
- isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
- if (!isQuietTime) {
- if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
- mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
- mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
- currentFingerCount < 2) {
- // Enter quiet time when exiting swipe or freeform state.
- // This is to prevent accidentally entering the hover state and flinging the
- // pointer when finishing a swipe and there is still one pointer left onscreen.
- isQuietTime = true;
- } else if (mPointerGesture.lastGestureMode ==
- PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
- currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
- // Enter quiet time when releasing the button and there are still two or more
- // fingers down. This may indicate that one finger was used to press the button
- // but it has not gone up yet.
- isQuietTime = true;
- }
- if (isQuietTime) {
- mPointerGesture.quietTime = when;
- }
- }
- }
+ const int32_t& activeTouchId = mPointerGesture.activeTouchId;
// Switch states based on button and pointer state.
- if (isQuietTime) {
+ if (checkForTouchpadQuietTime(when)) {
// Case 1: Quiet time. (QUIET)
ALOGD_IF(DEBUG_GESTURES, "Gestures: QUIET for next %0.3fms",
(mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
@@ -2844,24 +2861,9 @@
// Switch pointers if needed.
// Find the fastest pointer and follow it.
if (activeTouchId >= 0 && currentFingerCount > 1) {
- int32_t bestId = -1;
- float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- std::optional<float> vx =
- mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
- std::optional<float> vy =
- mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
- if (vx && vy) {
- float speed = hypotf(*vx, *vy);
- if (speed > bestSpeed) {
- bestId = id;
- bestSpeed = speed;
- }
- }
- }
+ const auto [bestId, bestSpeed] = getFastestFinger();
if (bestId >= 0 && bestId != activeTouchId) {
- mPointerGesture.activeTouchId = activeTouchId = bestId;
+ mPointerGesture.activeTouchId = bestId;
ALOGD_IF(DEBUG_GESTURES,
"Gestures: BUTTON_CLICK_OR_DRAG switched pointers, bestId=%d, "
"bestSpeed=%0.3f",
@@ -3027,335 +3029,7 @@
}
} else {
// Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
- // We need to provide feedback for each finger that goes down so we cannot wait
- // for the fingers to move before deciding what to do.
- //
- // The ambiguous case is deciding what to do when there are two fingers down but they
- // have not moved enough to determine whether they are part of a drag or part of a
- // freeform gesture, or just a press or long-press at the pointer location.
- //
- // When there are two fingers we start with the PRESS hypothesis and we generate a
- // down at the pointer location.
- //
- // When the two fingers move enough or when additional fingers are added, we make
- // a decision to transition into SWIPE or FREEFORM mode accordingly.
- ALOG_ASSERT(activeTouchId >= 0);
-
- bool settled = when >=
- mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
- if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
- mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
- mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
- *outFinishPreviousGesture = true;
- } else if (!settled && currentFingerCount > lastFingerCount) {
- // Additional pointers have gone down but not yet settled.
- // Reset the gesture.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: Resetting gesture since additional pointers went down for "
- "MULTITOUCH, settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime +
- mConfig.pointerGestureMultitouchSettleInterval - when) *
- 0.000001f);
- *outCancelPreviousGesture = true;
- } else {
- // Continue previous gesture.
- mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
- }
-
- if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
- mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
- mPointerGesture.activeGestureId = 0;
- mPointerGesture.referenceIdBits.clear();
- mPointerVelocityControl.reset();
-
- // Use the centroid and pointer location as the reference points for the gesture.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: Using centroid as reference for MULTITOUCH, settle time remaining "
- "%0.3fms",
- (mPointerGesture.firstTouchTime +
- mConfig.pointerGestureMultitouchSettleInterval - when) *
- 0.000001f);
- mCurrentRawState.rawPointerData
- .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
- &mPointerGesture.referenceTouchY);
- mPointerController->getPosition(&mPointerGesture.referenceGestureX,
- &mPointerGesture.referenceGestureY);
- }
-
- // Clear the reference deltas for fingers not yet included in the reference calculation.
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value &
- ~mPointerGesture.referenceIdBits.value);
- !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- mPointerGesture.referenceDeltas[id].dx = 0;
- mPointerGesture.referenceDeltas[id].dy = 0;
- }
- mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
-
- // Add delta for all fingers and calculate a common movement delta.
- float commonDeltaX = 0, commonDeltaY = 0;
- BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
- mCurrentCookedState.fingerIdBits.value);
- for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
- bool first = (idBits == commonIdBits);
- uint32_t id = idBits.clearFirstMarkedBit();
- const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
- const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx += cpd.x - lpd.x;
- delta.dy += cpd.y - lpd.y;
-
- if (first) {
- commonDeltaX = delta.dx;
- commonDeltaY = delta.dy;
- } else {
- commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
- commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
- }
- }
-
- // Consider transitions from PRESS to SWIPE or MULTITOUCH.
- if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
- float dist[MAX_POINTER_ID + 1];
- int32_t distOverThreshold = 0;
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale);
- if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
- distOverThreshold += 1;
- }
- }
-
- // Only transition when at least two pointers have moved further than
- // the minimum distance threshold.
- if (distOverThreshold >= 2) {
- if (currentFingerCount > 2) {
- // There are more than two pointers, switch to FREEFORM.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- } else {
- // There are exactly two pointers.
- BitSet32 idBits(mCurrentCookedState.fingerIdBits);
- uint32_t id1 = idBits.clearFirstMarkedBit();
- uint32_t id2 = idBits.firstMarkedBit();
- const RawPointerData::Pointer& p1 =
- mCurrentRawState.rawPointerData.pointerForId(id1);
- const RawPointerData::Pointer& p2 =
- mCurrentRawState.rawPointerData.pointerForId(id2);
- float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
- if (mutualDistance > mPointerGestureMaxSwipeWidth) {
- // There are two pointers but they are too far apart for a SWIPE,
- // switch to FREEFORM.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- mutualDistance, mPointerGestureMaxSwipeWidth);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- } else {
- // There are two pointers. Wait for both pointers to start moving
- // before deciding whether this is a SWIPE or FREEFORM gesture.
- float dist1 = dist[id1];
- float dist2 = dist[id2];
- if (dist1 >= mConfig.pointerGestureMultitouchMinDistance &&
- dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
- // Calculate the dot product of the displacement vectors.
- // When the vectors are oriented in approximately the same direction,
- // the angle betweeen them is near zero and the cosine of the angle
- // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) *
- // mag(v2).
- PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
- PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
- float dx1 = delta1.dx * mPointerXZoomScale;
- float dy1 = delta1.dy * mPointerYZoomScale;
- float dx2 = delta2.dx * mPointerXZoomScale;
- float dy2 = delta2.dy * mPointerYZoomScale;
- float dot = dx1 * dx2 + dy1 * dy2;
- float cosine = dot / (dist1 * dist2); // denominator always > 0
- if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
- // Pointers are moving in the same direction. Switch to SWIPE.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to SWIPE, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
- mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
- } else {
- // Pointers are moving in different directions. Switch to FREEFORM.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS transitioned to FREEFORM, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- }
- }
- }
- }
- }
- } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
- // Switch from SWIPE to FREEFORM if additional pointers go down.
- // Cancel previous gesture.
- if (currentFingerCount > 2) {
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
- *outCancelPreviousGesture = true;
- mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
- }
- }
-
- // Move the reference points based on the overall group motion of the fingers
- // except in PRESS mode while waiting for a transition to occur.
- if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
- (commonDeltaX || commonDeltaY)) {
- for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
- delta.dx = 0;
- delta.dy = 0;
- }
-
- mPointerGesture.referenceTouchX += commonDeltaX;
- mPointerGesture.referenceTouchY += commonDeltaY;
-
- commonDeltaX *= mPointerXMovementScale;
- commonDeltaY *= mPointerYMovementScale;
-
- rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
- mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
- mPointerGesture.referenceGestureX += commonDeltaX;
- mPointerGesture.referenceGestureY += commonDeltaY;
- }
-
- // Report gestures.
- if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
- mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
- // PRESS or SWIPE mode.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: PRESS or SWIPE activeTouchId=%d, activeGestureId=%d, "
- "currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
- mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
- mPointerGesture.currentGestureProperties[0].clear();
- mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
- mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
- mPointerGesture.referenceGestureX);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
- mPointerGesture.referenceGestureY);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- // FREEFORM mode.
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM activeTouchId=%d, activeGestureId=%d, "
- "currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
- ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
-
- mPointerGesture.currentGestureIdBits.clear();
-
- BitSet32 mappedTouchIdBits;
- BitSet32 usedGestureIdBits;
- if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
- // Initially, assign the active gesture id to the active touch point
- // if there is one. No other touch id bits are mapped yet.
- if (!*outCancelPreviousGesture) {
- mappedTouchIdBits.markBit(activeTouchId);
- usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
- mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
- mPointerGesture.activeGestureId;
- } else {
- mPointerGesture.activeGestureId = -1;
- }
- } else {
- // Otherwise, assume we mapped all touches from the previous frame.
- // Reuse all mappings that are still applicable.
- mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value &
- mCurrentCookedState.fingerIdBits.value;
- usedGestureIdBits = mPointerGesture.lastGestureIdBits;
-
- // Check whether we need to choose a new active gesture id because the
- // current went went up.
- for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value &
- ~mCurrentCookedState.fingerIdBits.value);
- !upTouchIdBits.isEmpty();) {
- uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
- uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
- if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
- mPointerGesture.activeGestureId = -1;
- break;
- }
- }
- }
-
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, "
- "usedGestureIdBits=0x%08x, activeGestureId=%d",
- mappedTouchIdBits.value, usedGestureIdBits.value,
- mPointerGesture.activeGestureId);
-
- BitSet32 idBits(mCurrentCookedState.fingerIdBits);
- for (uint32_t i = 0; i < currentFingerCount; i++) {
- uint32_t touchId = idBits.clearFirstMarkedBit();
- uint32_t gestureId;
- if (!mappedTouchIdBits.hasBit(touchId)) {
- gestureId = usedGestureIdBits.markFirstUnmarkedBit();
- mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
- } else {
- gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
- ALOGD_IF(DEBUG_GESTURES,
- "Gestures: FREEFORM existing mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
- }
- mPointerGesture.currentGestureIdBits.markBit(gestureId);
- mPointerGesture.currentGestureIdToIndex[gestureId] = i;
-
- const RawPointerData::Pointer& pointer =
- mCurrentRawState.rawPointerData.pointerForId(touchId);
- float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
- float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
- rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
-
- mPointerGesture.currentGestureProperties[i].clear();
- mPointerGesture.currentGestureProperties[i].id = gestureId;
- mPointerGesture.currentGestureProperties[i].toolType =
- AMOTION_EVENT_TOOL_TYPE_FINGER;
- mPointerGesture.currentGestureCoords[i].clear();
- mPointerGesture.currentGestureCoords[i]
- .setAxisValue(AMOTION_EVENT_AXIS_X,
- mPointerGesture.referenceGestureX + deltaX);
- mPointerGesture.currentGestureCoords[i]
- .setAxisValue(AMOTION_EVENT_AXIS_Y,
- mPointerGesture.referenceGestureY + deltaY);
- mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- 1.0f);
- }
-
- if (mPointerGesture.activeGestureId < 0) {
- mPointerGesture.activeGestureId =
- mPointerGesture.currentGestureIdBits.firstMarkedBit();
- ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d",
- mPointerGesture.activeGestureId);
- }
- }
+ prepareMultiFingerPointerGestures(when, outCancelPreviousGesture, outFinishPreviousGesture);
}
mPointerController->setButtonState(mCurrentRawState.buttonState);
@@ -3393,6 +3067,399 @@
return true;
}
+bool TouchInputMapper::checkForTouchpadQuietTime(nsecs_t when) {
+ if (mPointerGesture.activeTouchId < 0) {
+ mPointerGesture.resetQuietTime();
+ return false;
+ }
+
+ if (when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval) {
+ return true;
+ }
+
+ const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+ bool isQuietTime = false;
+ if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
+ mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
+ currentFingerCount < 2) {
+ // Enter quiet time when exiting swipe or freeform state.
+ // This is to prevent accidentally entering the hover state and flinging the
+ // pointer when finishing a swipe and there is still one pointer left onscreen.
+ isQuietTime = true;
+ } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
+ currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
+ // Enter quiet time when releasing the button and there are still two or more
+ // fingers down. This may indicate that one finger was used to press the button
+ // but it has not gone up yet.
+ isQuietTime = true;
+ }
+ if (isQuietTime) {
+ mPointerGesture.quietTime = when;
+ }
+ return isQuietTime;
+}
+
+std::pair<int32_t, float> TouchInputMapper::getFastestFinger() {
+ int32_t bestId = -1;
+ float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ std::optional<float> vx =
+ mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
+ std::optional<float> vy =
+ mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
+ if (vx && vy) {
+ float speed = hypotf(*vx, *vy);
+ if (speed > bestSpeed) {
+ bestId = id;
+ bestSpeed = speed;
+ }
+ }
+ }
+ return std::make_pair(bestId, bestSpeed);
+}
+
+void TouchInputMapper::prepareMultiFingerPointerGestures(nsecs_t when, bool* cancelPreviousGesture,
+ bool* finishPreviousGesture) {
+ // We need to provide feedback for each finger that goes down so we cannot wait for the fingers
+ // to move before deciding what to do.
+ //
+ // The ambiguous case is deciding what to do when there are two fingers down but they have not
+ // moved enough to determine whether they are part of a drag or part of a freeform gesture, or
+ // just a press or long-press at the pointer location.
+ //
+ // When there are two fingers we start with the PRESS hypothesis and we generate a down at the
+ // pointer location.
+ //
+ // When the two fingers move enough or when additional fingers are added, we make a decision to
+ // transition into SWIPE or FREEFORM mode accordingly.
+ const int32_t activeTouchId = mPointerGesture.activeTouchId;
+ ALOG_ASSERT(activeTouchId >= 0);
+
+ const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
+ const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
+ bool settled =
+ when >= mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
+ mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
+ *finishPreviousGesture = true;
+ } else if (!settled && currentFingerCount > lastFingerCount) {
+ // Additional pointers have gone down but not yet settled.
+ // Reset the gesture.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: Resetting gesture since additional pointers went down for "
+ "MULTITOUCH, settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+ when) * 0.000001f);
+ *cancelPreviousGesture = true;
+ } else {
+ // Continue previous gesture.
+ mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
+ }
+
+ if (*finishPreviousGesture || *cancelPreviousGesture) {
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
+ mPointerGesture.activeGestureId = 0;
+ mPointerGesture.referenceIdBits.clear();
+ mPointerVelocityControl.reset();
+
+ // Use the centroid and pointer location as the reference points for the gesture.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: Using centroid as reference for MULTITOUCH, settle time remaining "
+ "%0.3fms",
+ (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
+ when) * 0.000001f);
+ mCurrentRawState.rawPointerData
+ .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
+ &mPointerGesture.referenceTouchY);
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
+ }
+
+ // Clear the reference deltas for fingers not yet included in the reference calculation.
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value &
+ ~mPointerGesture.referenceIdBits.value);
+ !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ mPointerGesture.referenceDeltas[id].dx = 0;
+ mPointerGesture.referenceDeltas[id].dy = 0;
+ }
+ mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
+
+ // Add delta for all fingers and calculate a common movement delta.
+ int32_t commonDeltaRawX = 0, commonDeltaRawY = 0;
+ BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
+ mCurrentCookedState.fingerIdBits.value);
+ for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
+ bool first = (idBits == commonIdBits);
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id);
+ const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id);
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx += cpd.x - lpd.x;
+ delta.dy += cpd.y - lpd.y;
+
+ if (first) {
+ commonDeltaRawX = delta.dx;
+ commonDeltaRawY = delta.dy;
+ } else {
+ commonDeltaRawX = calculateCommonVector(commonDeltaRawX, delta.dx);
+ commonDeltaRawY = calculateCommonVector(commonDeltaRawY, delta.dy);
+ }
+ }
+
+ // Consider transitions from PRESS to SWIPE or MULTITOUCH.
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS) {
+ float dist[MAX_POINTER_ID + 1];
+ int32_t distOverThreshold = 0;
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale);
+ if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
+ distOverThreshold += 1;
+ }
+ }
+
+ // Only transition when at least two pointers have moved further than
+ // the minimum distance threshold.
+ if (distOverThreshold >= 2) {
+ if (currentFingerCount > 2) {
+ // There are more than two pointers, switch to FREEFORM.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ } else {
+ // There are exactly two pointers.
+ BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+ uint32_t id1 = idBits.clearFirstMarkedBit();
+ uint32_t id2 = idBits.firstMarkedBit();
+ const RawPointerData::Pointer& p1 =
+ mCurrentRawState.rawPointerData.pointerForId(id1);
+ const RawPointerData::Pointer& p2 =
+ mCurrentRawState.rawPointerData.pointerForId(id2);
+ float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
+ if (mutualDistance > mPointerGestureMaxSwipeWidth) {
+ // There are two pointers but they are too far apart for a SWIPE,
+ // switch to FREEFORM.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+ mutualDistance, mPointerGestureMaxSwipeWidth);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ } else {
+ // There are two pointers. Wait for both pointers to start moving
+ // before deciding whether this is a SWIPE or FREEFORM gesture.
+ float dist1 = dist[id1];
+ float dist2 = dist[id2];
+ if (dist1 >= mConfig.pointerGestureMultitouchMinDistance &&
+ dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
+ // Calculate the dot product of the displacement vectors.
+ // When the vectors are oriented in approximately the same direction,
+ // the angle betweeen them is near zero and the cosine of the angle
+ // approaches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) *
+ // mag(v2).
+ PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+ PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+ float dx1 = delta1.dx * mPointerXZoomScale;
+ float dy1 = delta1.dy * mPointerYZoomScale;
+ float dx2 = delta2.dx * mPointerXZoomScale;
+ float dy2 = delta2.dy * mPointerYZoomScale;
+ float dot = dx1 * dx2 + dy1 * dy2;
+ float cosine = dot / (dist1 * dist2); // denominator always > 0
+ if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
+ // Pointers are moving in the same direction. Switch to SWIPE.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
+ } else {
+ // Pointers are moving in different directions. Switch to FREEFORM.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ }
+ }
+ }
+ }
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+ // Switch from SWIPE to FREEFORM if additional pointers go down.
+ // Cancel previous gesture.
+ if (currentFingerCount > 2) {
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ *cancelPreviousGesture = true;
+ mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
+ }
+ }
+
+ // Move the reference points based on the overall group motion of the fingers
+ // except in PRESS mode while waiting for a transition to occur.
+ if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
+ (commonDeltaRawX || commonDeltaRawY)) {
+ for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+ delta.dx = 0;
+ delta.dy = 0;
+ }
+
+ mPointerGesture.referenceTouchX += commonDeltaRawX;
+ mPointerGesture.referenceTouchY += commonDeltaRawY;
+
+ float commonDeltaX = commonDeltaRawX * mPointerXMovementScale;
+ float commonDeltaY = commonDeltaRawY * mPointerYMovementScale;
+
+ rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
+ mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
+
+ mPointerGesture.referenceGestureX += commonDeltaX;
+ mPointerGesture.referenceGestureY += commonDeltaY;
+ }
+
+ // Report gestures.
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
+ mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+ // PRESS or SWIPE mode.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: PRESS or SWIPE activeTouchId=%d, activeGestureId=%d, "
+ "currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+ mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
+ mPointerGesture.currentGestureProperties[0].clear();
+ mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
+ mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[0].clear();
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY);
+ mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+ float xOffset = static_cast<float>(commonDeltaRawX) /
+ (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue);
+ float yOffset = static_cast<float>(commonDeltaRawY) /
+ (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue);
+ mPointerGesture.currentGestureCoords[0]
+ .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
+ mPointerGesture.currentGestureCoords[0]
+ .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
+ }
+ } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
+ // FREEFORM mode.
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM activeTouchId=%d, activeGestureId=%d, "
+ "currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
+
+ mPointerGesture.currentGestureIdBits.clear();
+
+ BitSet32 mappedTouchIdBits;
+ BitSet32 usedGestureIdBits;
+ if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
+ // Initially, assign the active gesture id to the active touch point
+ // if there is one. No other touch id bits are mapped yet.
+ if (!*cancelPreviousGesture) {
+ mappedTouchIdBits.markBit(activeTouchId);
+ usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
+ mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
+ mPointerGesture.activeGestureId;
+ } else {
+ mPointerGesture.activeGestureId = -1;
+ }
+ } else {
+ // Otherwise, assume we mapped all touches from the previous frame.
+ // Reuse all mappings that are still applicable.
+ mappedTouchIdBits.value =
+ mLastCookedState.fingerIdBits.value & mCurrentCookedState.fingerIdBits.value;
+ usedGestureIdBits = mPointerGesture.lastGestureIdBits;
+
+ // Check whether we need to choose a new active gesture id because the
+ // current went went up.
+ for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value &
+ ~mCurrentCookedState.fingerIdBits.value);
+ !upTouchIdBits.isEmpty();) {
+ uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
+ uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
+ if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
+ mPointerGesture.activeGestureId = -1;
+ break;
+ }
+ }
+ }
+
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+ "activeGestureId=%d",
+ mappedTouchIdBits.value, usedGestureIdBits.value, mPointerGesture.activeGestureId);
+
+ BitSet32 idBits(mCurrentCookedState.fingerIdBits);
+ for (uint32_t i = 0; i < currentFingerCount; i++) {
+ uint32_t touchId = idBits.clearFirstMarkedBit();
+ uint32_t gestureId;
+ if (!mappedTouchIdBits.hasBit(touchId)) {
+ gestureId = usedGestureIdBits.markFirstUnmarkedBit();
+ mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d", touchId,
+ gestureId);
+ } else {
+ gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
+ ALOGD_IF(DEBUG_GESTURES,
+ "Gestures: FREEFORM existing mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+ }
+ mPointerGesture.currentGestureIdBits.markBit(gestureId);
+ mPointerGesture.currentGestureIdToIndex[gestureId] = i;
+
+ const RawPointerData::Pointer& pointer =
+ mCurrentRawState.rawPointerData.pointerForId(touchId);
+ float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
+ float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
+
+ mPointerGesture.currentGestureProperties[i].clear();
+ mPointerGesture.currentGestureProperties[i].id = gestureId;
+ mPointerGesture.currentGestureProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ mPointerGesture.currentGestureCoords[i].clear();
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
+ mPointerGesture.referenceGestureX +
+ deltaX);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
+ mPointerGesture.referenceGestureY +
+ deltaY);
+ mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+ }
+
+ if (mPointerGesture.activeGestureId < 0) {
+ mPointerGesture.activeGestureId = mPointerGesture.currentGestureIdBits.firstMarkedBit();
+ ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d",
+ mPointerGesture.activeGestureId);
+ }
+ }
+}
+
void TouchInputMapper::moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId) {
const RawPointerData::Pointer& currentPointer =
mCurrentRawState.rawPointerData.pointerForId(pointerId);
@@ -3407,7 +3474,8 @@
mPointerController->move(deltaX, deltaY);
}
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
@@ -3436,14 +3504,16 @@
hovering = false;
}
- dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
+ return dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
}
-void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
- abortPointerSimple(when, readTime, policyFlags);
+std::list<NotifyArgs> TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ return abortPointerSimple(when, readTime, policyFlags);
}
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
@@ -3478,17 +3548,24 @@
hovering = false;
}
- dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
+ return dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
}
-void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
- abortPointerSimple(when, readTime, policyFlags);
+std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out = abortPointerSimple(when, readTime, policyFlags);
mPointerVelocityControl.reset();
+
+ return out;
}
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- bool down, bool hovering) {
+std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags, bool down,
+ bool hovering) {
+ LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER,
+ "%s cannot be used when the device is not in POINTER mode.", __func__);
+ std::list<NotifyArgs> out;
int32_t metaState = getContext()->getGlobalMetaState();
if (down || hovering) {
@@ -3508,28 +3585,29 @@
mPointerSimple.down = false;
// Send up.
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
- mLastRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
- &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0,
+ 0, metaState, mLastRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {}));
}
if (mPointerSimple.hovering && !hovering) {
mPointerSimple.hovering = false;
// Send hover exit.
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
- metaState, mLastRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
- &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+ mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {}));
}
if (down) {
@@ -3538,25 +3616,26 @@
mPointerSimple.downTime = when;
// Send down.
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
- metaState, mCurrentRawState.buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
- yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {}));
}
// Send move.
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &mPointerSimple.currentCoords, mOrientedXPrecision,
- mOrientedYPrecision, xCursorPosition, yCursorPosition,
- mPointerSimple.downTime, /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {}));
}
if (hovering) {
@@ -3564,25 +3643,26 @@
mPointerSimple.hovering = true;
// Send hover enter.
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
- metaState, mCurrentRawState.buttonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
- mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
- yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
+ mCurrentRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties,
+ &mPointerSimple.currentCoords, mOrientedXPrecision,
+ mOrientedYPrecision, xCursorPosition, yCursorPosition,
+ mPointerSimple.downTime, /* videoFrames */ {}));
}
// Send hover move.
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
- metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &mPointerSimple.currentCoords, mOrientedXPrecision,
- mOrientedYPrecision, xCursorPosition, yCursorPosition,
- mPointerSimple.downTime, /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(
+ NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}));
}
if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3597,40 +3677,59 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
- &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
- xCursorPosition, yCursorPosition, mPointerSimple.downTime,
- /* videoFrames */ {});
- getListener().notifyMotion(&args);
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL,
+ 0, 0, metaState, mCurrentRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.currentProperties, &pointerCoords,
+ mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+ yCursorPosition, mPointerSimple.downTime,
+ /* videoFrames */ {}));
}
// Save state.
if (down || hovering) {
mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+ mPointerSimple.displayId = displayId;
+ mPointerSimple.source = mSource;
+ mPointerSimple.lastCursorX = xCursorPosition;
+ mPointerSimple.lastCursorY = yCursorPosition;
} else {
mPointerSimple.reset();
}
+ return out;
}
-void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
- mPointerSimple.currentCoords.clear();
- mPointerSimple.currentProperties.clear();
-
- dispatchPointerSimple(when, readTime, policyFlags, false, false);
+std::list<NotifyArgs> TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
+ std::list<NotifyArgs> out;
+ if (mPointerSimple.down || mPointerSimple.hovering) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mPointerSimple.source, mPointerSimple.displayId, policyFlags,
+ AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+ metaState, mLastRawState.buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
+ mOrientedXPrecision, mOrientedYPrecision,
+ mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
+ mPointerSimple.downTime,
+ /* videoFrames */ {}));
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+ }
+ mPointerSimple.reset();
+ return out;
}
-void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- uint32_t source, int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, const PointerProperties* properties,
- const PointerCoords* coords, const uint32_t* idToIndex,
- BitSet32 idBits, int32_t changedId, float xPrecision,
- float yPrecision, nsecs_t downTime,
- MotionClassification classification) {
+NotifyMotionArgs TouchInputMapper::dispatchMotion(
+ nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+ int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
+ const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+ float yPrecision, nsecs_t downTime, MotionClassification classification) {
PointerCoords pointerCoords[MAX_POINTERS];
PointerProperties pointerProperties[MAX_POINTERS];
uint32_t pointerCount = 0;
@@ -3676,47 +3775,18 @@
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
[this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
- NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
- policyFlags, action, actionButton, flags, metaState, buttonState,
- classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
- xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
- std::move(frames));
- getListener().notifyMotion(&args);
+ return NotifyMotionArgs(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
+ policyFlags, action, actionButton, flags, metaState, buttonState,
+ classification, edgeFlags, pointerCount, pointerProperties,
+ pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+ downTime, std::move(frames));
}
-bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
- const PointerCoords* inCoords,
- const uint32_t* inIdToIndex,
- PointerProperties* outProperties,
- PointerCoords* outCoords, const uint32_t* outIdToIndex,
- BitSet32 idBits) const {
- bool changed = false;
- while (!idBits.isEmpty()) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t inIndex = inIdToIndex[id];
- uint32_t outIndex = outIdToIndex[id];
-
- const PointerProperties& curInProperties = inProperties[inIndex];
- const PointerCoords& curInCoords = inCoords[inIndex];
- PointerProperties& curOutProperties = outProperties[outIndex];
- PointerCoords& curOutCoords = outCoords[outIndex];
-
- if (curInProperties != curOutProperties) {
- curOutProperties.copyFrom(curInProperties);
- changed = true;
- }
-
- if (curInCoords != curOutCoords) {
- curOutCoords.copyFrom(curInCoords);
- changed = true;
- }
- }
- return changed;
-}
-
-void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
- abortPointerUsage(when, readTime, 0 /*policyFlags*/);
- abortTouches(when, readTime, 0 /* policyFlags*/);
+std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ out += abortPointerUsage(when, readTime, 0 /*policyFlags*/);
+ out += abortTouches(when, readTime, 0 /* policyFlags*/);
+ return out;
}
// Transform input device coordinates to display panel coordinates.
@@ -3733,19 +3803,19 @@
// 180 - reverse x, y.
// 270 - swap x/y and reverse x.
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_0:
+ case ui::ROTATION_0:
x = xScaled;
y = yScaled;
break;
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
y = xScaledMax;
x = yScaled;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = xScaledMax;
y = yScaledMax;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
y = xScaled;
x = yScaledMax;
break;
@@ -3759,9 +3829,8 @@
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
- xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
- yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
+ isPointInRect(mPhysicalFrameInDisplay, xScaled, yScaled);
}
const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 31806e1..34ba625 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <ui/Rotation.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
@@ -27,55 +28,65 @@
namespace android {
+// Maximum amount of latency to add to touch events while waiting for data from an
+// external stylus.
+static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72);
+
+// Maximum amount of time to wait on touch data before pushing out new pressure data.
+static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20);
+
/* Raw axis information from the driver. */
struct RawPointerAxes {
- RawAbsoluteAxisInfo x;
- RawAbsoluteAxisInfo y;
- RawAbsoluteAxisInfo pressure;
- RawAbsoluteAxisInfo touchMajor;
- RawAbsoluteAxisInfo touchMinor;
- RawAbsoluteAxisInfo toolMajor;
- RawAbsoluteAxisInfo toolMinor;
- RawAbsoluteAxisInfo orientation;
- RawAbsoluteAxisInfo distance;
- RawAbsoluteAxisInfo tiltX;
- RawAbsoluteAxisInfo tiltY;
- RawAbsoluteAxisInfo trackingId;
- RawAbsoluteAxisInfo slot;
+ RawAbsoluteAxisInfo x{};
+ RawAbsoluteAxisInfo y{};
+ RawAbsoluteAxisInfo pressure{};
+ RawAbsoluteAxisInfo touchMajor{};
+ RawAbsoluteAxisInfo touchMinor{};
+ RawAbsoluteAxisInfo toolMajor{};
+ RawAbsoluteAxisInfo toolMinor{};
+ RawAbsoluteAxisInfo orientation{};
+ RawAbsoluteAxisInfo distance{};
+ RawAbsoluteAxisInfo tiltX{};
+ RawAbsoluteAxisInfo tiltY{};
+ RawAbsoluteAxisInfo trackingId{};
+ RawAbsoluteAxisInfo slot{};
- RawPointerAxes();
inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
- void clear();
+ inline void clear() { *this = RawPointerAxes(); }
};
+using PropertiesArray = std::array<PointerProperties, MAX_POINTERS>;
+using CoordsArray = std::array<PointerCoords, MAX_POINTERS>;
+using IdToIndexArray = std::array<uint32_t, MAX_POINTER_ID + 1>;
+
/* Raw data for a collection of pointers including a pointer id mapping table. */
struct RawPointerData {
struct Pointer {
- uint32_t id;
- int32_t x;
- int32_t y;
- int32_t pressure;
- int32_t touchMajor;
- int32_t touchMinor;
- int32_t toolMajor;
- int32_t toolMinor;
- int32_t orientation;
- int32_t distance;
- int32_t tiltX;
- int32_t tiltY;
- int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
- bool isHovering;
+ uint32_t id{0xFFFFFFFF};
+ int32_t x{};
+ int32_t y{};
+ int32_t pressure{};
+ int32_t touchMajor{};
+ int32_t touchMinor{};
+ int32_t toolMajor{};
+ int32_t toolMinor{};
+ int32_t orientation{};
+ int32_t distance{};
+ int32_t tiltX{};
+ int32_t tiltY{};
+ // A fully decoded AMOTION_EVENT_TOOL_TYPE constant.
+ int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN};
+ bool isHovering{false};
};
- uint32_t pointerCount;
- Pointer pointers[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
+ uint32_t pointerCount{};
+ std::array<Pointer, MAX_POINTERS> pointers{};
+ BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{};
+ IdToIndexArray idToIndex{};
- RawPointerData();
- void clear();
- void copyFrom(const RawPointerData& other);
+ inline void clear() { *this = RawPointerData(); }
+
void getCentroidOfTouchingPointers(float* outX, float* outY) const;
inline void markIdBit(uint32_t id, bool isHovering) {
@@ -99,15 +110,13 @@
/* Cooked data for a collection of pointers including a pointer id mapping table. */
struct CookedPointerData {
- uint32_t pointerCount;
- PointerProperties pointerProperties[MAX_POINTERS];
- PointerCoords pointerCoords[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits;
- uint32_t idToIndex[MAX_POINTER_ID + 1];
+ uint32_t pointerCount{};
+ PropertiesArray pointerProperties{};
+ CoordsArray pointerCoords{};
+ BitSet32 hoveringIdBits{}, touchingIdBits{}, canceledIdBits{}, validIdBits{};
+ IdToIndexArray idToIndex{};
- CookedPointerData();
- void clear();
- void copyFrom(const CookedPointerData& other);
+ inline void clear() { *this = CookedPointerData(); }
inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
return pointerCoords[idToIndex[id]];
@@ -140,18 +149,21 @@
uint32_t getSources() const override;
void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
void dump(std::string& dump) override;
- void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
- void reset(nsecs_t when) override;
- void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
+ const InputReaderConfiguration* config,
+ uint32_t changes) override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
bool markSupportedKeyCodes(uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) override;
- void cancelTouch(nsecs_t when, nsecs_t readTime) override;
- void timeoutExpired(nsecs_t when) override;
- void updateExternalStylusState(const StylusState& state) override;
+ [[nodiscard]] std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) override;
+ [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(
+ const StylusState& state) override;
std::optional<int32_t> getAssociatedDisplayId() override;
protected:
@@ -207,15 +219,7 @@
bool associatedDisplayIsExternal;
bool orientationAware;
- enum class Orientation : int32_t {
- ORIENTATION_0 = DISPLAY_ORIENTATION_0,
- ORIENTATION_90 = DISPLAY_ORIENTATION_90,
- ORIENTATION_180 = DISPLAY_ORIENTATION_180,
- ORIENTATION_270 = DISPLAY_ORIENTATION_270,
-
- ftl_last = ORIENTATION_270
- };
- Orientation orientation;
+ ui::Rotation orientation;
bool hasButtonUnderPad;
std::string uniqueDisplayId;
@@ -229,6 +233,12 @@
GestureMode gestureMode;
bool wake;
+
+ // Whether the device supports the Universal Stylus Initiative (USI) protocol for styluses.
+ bool supportsUsi;
+
+ // Allows touches while the display is off.
+ bool enableForInactiveViewport;
} mParameters;
// Immutable calibration parameters in parsed form.
@@ -308,63 +318,33 @@
RawPointerAxes mRawPointerAxes;
struct RawState {
- nsecs_t when;
- nsecs_t readTime;
+ nsecs_t when{std::numeric_limits<nsecs_t>::min()};
+ nsecs_t readTime{};
// Raw pointer sample data.
- RawPointerData rawPointerData;
+ RawPointerData rawPointerData{};
- int32_t buttonState;
+ int32_t buttonState{};
// Scroll state.
- int32_t rawVScroll;
- int32_t rawHScroll;
+ int32_t rawVScroll{};
+ int32_t rawHScroll{};
- void copyFrom(const RawState& other) {
- when = other.when;
- readTime = other.readTime;
- rawPointerData.copyFrom(other.rawPointerData);
- buttonState = other.buttonState;
- rawVScroll = other.rawVScroll;
- rawHScroll = other.rawHScroll;
- }
-
- void clear() {
- when = 0;
- readTime = 0;
- rawPointerData.clear();
- buttonState = 0;
- rawVScroll = 0;
- rawHScroll = 0;
- }
+ inline void clear() { *this = RawState(); }
};
struct CookedState {
// Cooked pointer sample data.
- CookedPointerData cookedPointerData;
+ CookedPointerData cookedPointerData{};
// Id bits used to differentiate fingers, stylus and mouse tools.
- BitSet32 fingerIdBits;
- BitSet32 stylusIdBits;
- BitSet32 mouseIdBits;
+ BitSet32 fingerIdBits{};
+ BitSet32 stylusIdBits{};
+ BitSet32 mouseIdBits{};
- int32_t buttonState;
+ int32_t buttonState{};
- void copyFrom(const CookedState& other) {
- cookedPointerData.copyFrom(other.cookedPointerData);
- fingerIdBits = other.fingerIdBits;
- stylusIdBits = other.stylusIdBits;
- mouseIdBits = other.mouseIdBits;
- buttonState = other.buttonState;
- }
-
- void clear() {
- cookedPointerData.clear();
- fingerIdBits.clear();
- stylusIdBits.clear();
- mouseIdBits.clear();
- buttonState = 0;
- }
+ inline void clear() { *this = CookedState(); }
};
std::vector<RawState> mRawStatesPending;
@@ -375,9 +355,14 @@
// State provided by an external stylus
StylusState mExternalStylusState;
- int64_t mExternalStylusId;
+ // If an external stylus is capable of reporting pointer-specific data like pressure, we will
+ // attempt to fuse the pointer data reported by the stylus to the first touch pointer. This is
+ // the id of the pointer to which the external stylus data is fused.
+ std::optional<uint32_t> mFusedStylusPointerId;
nsecs_t mExternalStylusFusionTimeout;
bool mExternalStylusDataPending;
+ // A subset of the buttons in mCurrentRawState that came from an external stylus.
+ int32_t mExternalStylusButtonsApplied;
// True if we sent a HOVER_ENTER event.
bool mSentHoverEnter;
@@ -420,22 +405,19 @@
// The components of the viewport are specified in the display's rotated orientation.
DisplayViewport mViewport;
- // The width and height are obtained from the viewport and are specified
- // in the natural orientation.
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
+ // We refer to the display as being in the "natural orientation" when there is no rotation
+ // applied. The display size obtained from the viewport in the natural orientation.
+ // Always starts at (0, 0).
+ ui::Size mDisplayBounds{ui::kInvalidSize};
- // The physical frame is the rectangle in the display's coordinate space that maps to the
+ // The physical frame is the rectangle in the natural display's coordinate space that maps to
// the logical display frame.
- int32_t mPhysicalWidth;
- int32_t mPhysicalHeight;
- int32_t mPhysicalLeft;
- int32_t mPhysicalTop;
+ Rect mPhysicalFrameInDisplay{Rect::INVALID_RECT};
// The orientation of the input device relative to that of the display panel. It specifies
// the rotation of the input device coordinates required to produce the display panel
// orientation, so it will depend on whether the device is orientation aware.
- int32_t mInputDeviceOrientation;
+ ui::Rotation mInputDeviceOrientation;
// Translation and scaling factors, orientation-independent.
float mXScale;
@@ -520,9 +502,9 @@
float mPointerGestureMaxSwipeWidth;
struct PointerDistanceHeapElement {
- uint32_t currentPointerIndex : 8;
- uint32_t lastPointerIndex : 8;
- uint64_t distance : 48; // squared distance
+ uint32_t currentPointerIndex : 8 {};
+ uint32_t lastPointerIndex : 8 {};
+ uint64_t distance : 48 {}; // squared distance
};
enum class PointerUsage {
@@ -619,15 +601,15 @@
// Pointer coords and ids for the current and previous pointer gesture.
Mode currentGestureMode;
BitSet32 currentGestureIdBits;
- uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1];
- PointerProperties currentGestureProperties[MAX_POINTERS];
- PointerCoords currentGestureCoords[MAX_POINTERS];
+ IdToIndexArray currentGestureIdToIndex{};
+ PropertiesArray currentGestureProperties{};
+ CoordsArray currentGestureCoords{};
Mode lastGestureMode;
BitSet32 lastGestureIdBits;
- uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1];
- PointerProperties lastGestureProperties[MAX_POINTERS];
- PointerCoords lastGestureCoords[MAX_POINTERS];
+ IdToIndexArray lastGestureIdToIndex{};
+ PropertiesArray lastGestureProperties{};
+ CoordsArray lastGestureCoords{};
// Time the pointer gesture last went down.
nsecs_t downTime;
@@ -701,6 +683,12 @@
// Time the pointer last went down.
nsecs_t downTime;
+ // Values reported for the last pointer event.
+ uint32_t source;
+ int32_t displayId;
+ float lastCursorX;
+ float lastCursorY;
+
void reset() {
currentCoords.clear();
currentProperties.clear();
@@ -709,6 +697,10 @@
down = false;
hovering = false;
downTime = 0;
+ source = 0;
+ displayId = ADISPLAY_ID_NONE;
+ lastCursorX = 0.f;
+ lastCursorY = 0.f;
}
} mPointerSimple;
@@ -726,47 +718,75 @@
void initializeOrientedRanges();
void initializeSizeRanges();
- void sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
- bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void processRawTouches(bool timeout);
- void cookAndDispatch(nsecs_t when, nsecs_t readTime);
- void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- int32_t keyEventAction, int32_t keyEventFlags);
+ [[nodiscard]] std::list<NotifyArgs> consumeRawTouches(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags, bool& outConsumed);
+ [[nodiscard]] std::list<NotifyArgs> processRawTouches(bool timeout);
+ [[nodiscard]] std::list<NotifyArgs> cookAndDispatch(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] NotifyKeyArgs dispatchVirtualKey(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags, int32_t keyEventAction,
+ int32_t keyEventFlags);
- void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchTouches(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchHoverExit(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchButtonRelease(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchButtonPress(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
void cookPointerData();
- void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> abortTouches(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
- void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- PointerUsage pointerUsage);
- void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags,
+ PointerUsage pointerUsage);
+ [[nodiscard]] std::list<NotifyArgs> abortPointerUsage(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
- void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
- bool isTimeout);
- void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchPointerGestures(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags,
+ bool isTimeout);
+ [[nodiscard]] std::list<NotifyArgs> abortPointerGestures(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
bool* outFinishPreviousGesture, bool isTimeout);
+ // Returns true if we're in a period of "quiet time" when touchpad gestures should be ignored.
+ bool checkForTouchpadQuietTime(nsecs_t when);
+
+ std::pair<int32_t, float> getFastestFinger();
+
+ void prepareMultiFingerPointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
+ bool* outFinishPreviousGesture);
+
// Moves the on-screen mouse pointer based on the movement of the pointer of the given ID
// between the last and current events. Uses a relative motion.
void moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId);
- void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> abortPointerStylus(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
- void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchPointerMouse(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> abortPointerMouse(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
- void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down,
- bool hovering);
- void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags, bool down,
+ bool hovering);
+ [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags);
+ // Attempts to assign a pointer id to the external stylus. Returns true if the state should be
+ // withheld from further processing while waiting for data from the stylus.
bool assignExternalStylusId(const RawState& state, bool timeout);
void applyExternalStylusButtonState(nsecs_t when);
void applyExternalStylusTouchState(nsecs_t when);
@@ -775,19 +795,12 @@
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
// method will take care of setting the index and transmuting the action to DOWN or UP
// it is the first / last pointer to go down / up.
- void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
- const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
- int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime,
- MotionClassification classification);
-
- // Updates pointer coords and properties for pointers with specified ids that have moved.
- // Returns true if any of them changed.
- bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords,
- const uint32_t* inIdToIndex, PointerProperties* outProperties,
- PointerCoords* outCoords, const uint32_t* outIdToIndex,
- BitSet32 idBits) const;
+ [[nodiscard]] NotifyMotionArgs dispatchMotion(
+ nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+ int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
+ const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
+ float yPrecision, nsecs_t downTime, MotionClassification classification);
// Returns if this touch device is a touch screen with an associated display.
bool isTouchScreen();
@@ -800,7 +813,6 @@
static void assignPointerIds(const RawState& last, RawState& current);
- const char* modeToString(DeviceMode deviceMode);
void rotateAndScale(float& x, float& y) const;
};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
new file mode 100644
index 0000000..de6e4b0
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -0,0 +1,365 @@
+/*
+ * 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 "../Macros.h"
+
+#include <chrono>
+
+#include <android/input.h>
+#include <log/log_main.h>
+#include "TouchCursorInputMapperCommon.h"
+#include "TouchpadInputMapper.h"
+
+namespace android {
+
+namespace {
+
+short getMaxTouchCount(const InputDeviceContext& context) {
+ if (context.hasKeyCode(BTN_TOOL_QUINTTAP)) return 5;
+ if (context.hasKeyCode(BTN_TOOL_QUADTAP)) return 4;
+ if (context.hasKeyCode(BTN_TOOL_TRIPLETAP)) return 3;
+ if (context.hasKeyCode(BTN_TOOL_DOUBLETAP)) return 2;
+ if (context.hasKeyCode(BTN_TOOL_FINGER)) return 1;
+ return 0;
+}
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+ HardwareProperties props;
+ RawAbsoluteAxisInfo absMtPositionX;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ props.left = absMtPositionX.minValue;
+ props.right = absMtPositionX.maxValue;
+ props.res_x = absMtPositionX.resolution;
+
+ RawAbsoluteAxisInfo absMtPositionY;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ props.top = absMtPositionY.minValue;
+ props.bottom = absMtPositionY.maxValue;
+ props.res_y = absMtPositionY.resolution;
+
+ RawAbsoluteAxisInfo absMtOrientation;
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+ props.orientation_minimum = absMtOrientation.minValue;
+ props.orientation_maximum = absMtOrientation.maxValue;
+
+ RawAbsoluteAxisInfo absMtSlot;
+ context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+ props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ props.max_touch_cnt = getMaxTouchCount(context);
+
+ // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+ // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+ // that did this, so assume false.
+ props.supports_t5r2 = false;
+
+ props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+ props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+ // Mouse-only properties, which will always be false.
+ props.has_wheel = false;
+ props.wheel_is_hi_res = false;
+
+ // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+ // are haptic.
+ props.is_haptic_pad = false;
+ return props;
+}
+
+void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
+ TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
+ mapper->consumeGesture(gesture);
+}
+
+uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
+ switch (gesturesButton) {
+ case GESTURES_BUTTON_LEFT:
+ return AMOTION_EVENT_BUTTON_PRIMARY;
+ case GESTURES_BUTTON_MIDDLE:
+ return AMOTION_EVENT_BUTTON_TERTIARY;
+ case GESTURES_BUTTON_RIGHT:
+ return AMOTION_EVENT_BUTTON_SECONDARY;
+ case GESTURES_BUTTON_BACK:
+ return AMOTION_EVENT_BUTTON_BACK;
+ case GESTURES_BUTTON_FORWARD:
+ return AMOTION_EVENT_BUTTON_FORWARD;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
+ : InputMapper(deviceContext),
+ mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
+ mPointerController(getContext()->getPointerController(getDeviceId())),
+ mTouchButtonAccumulator(deviceContext) {
+ mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
+ mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+ // Even though we don't explicitly delete copy/move semantics, it's safe to
+ // give away a pointer to TouchpadInputMapper here because
+ // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
+ // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
+ mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
+ // TODO(b/251196347): set a property provider, so we can change gesture properties.
+ // TODO(b/251196347): set a timer provider, so the library can use timers.
+
+ RawAbsoluteAxisInfo slotAxisInfo;
+ getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+ ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+ "properly.",
+ getDeviceName().c_str());
+ }
+ mMotionAccumulator.configure(getDeviceContext(), slotAxisInfo.maxValue + 1, true);
+ mTouchButtonAccumulator.configure();
+}
+
+TouchpadInputMapper::~TouchpadInputMapper() {
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+}
+
+uint32_t TouchpadInputMapper::getSources() const {
+ return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
+ mCursorButtonAccumulator.reset(getDeviceContext());
+ mTouchButtonAccumulator.reset();
+ mMscTimestamp = 0;
+
+ mButtonState = 0;
+ return InputMapper::reset(when);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+ std::list<NotifyArgs> out = {};
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out = sync(rawEvent->when, rawEvent->readTime);
+ }
+ if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+ mMscTimestamp = rawEvent->value;
+ }
+ mCursorButtonAccumulator.process(rawEvent);
+ mMotionAccumulator.process(rawEvent);
+ mTouchButtonAccumulator.process(rawEvent);
+ return out;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ HardwareState hwState;
+ // The gestures library uses doubles to represent timestamps in seconds.
+ hwState.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+ hwState.msc_timestamp =
+ std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();
+
+ hwState.buttons_down = 0;
+ if (mCursorButtonAccumulator.isLeftPressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_LEFT;
+ }
+ if (mCursorButtonAccumulator.isMiddlePressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_MIDDLE;
+ }
+ if (mCursorButtonAccumulator.isRightPressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_RIGHT;
+ }
+ if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_BACK;
+ }
+ if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
+ hwState.buttons_down |= GESTURES_BUTTON_FORWARD;
+ }
+
+ std::vector<FingerState> fingers;
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+ if (slot.isInUse()) {
+ FingerState& fingerState = fingers.emplace_back();
+ fingerState = {};
+ fingerState.touch_major = slot.getTouchMajor();
+ fingerState.touch_minor = slot.getTouchMinor();
+ fingerState.width_major = slot.getToolMajor();
+ fingerState.width_minor = slot.getToolMinor();
+ fingerState.pressure = slot.getPressure();
+ fingerState.orientation = slot.getOrientation();
+ fingerState.position_x = slot.getX();
+ fingerState.position_y = slot.getY();
+ fingerState.tracking_id = slot.getTrackingId();
+ }
+ }
+ hwState.fingers = fingers.data();
+ hwState.finger_cnt = fingers.size();
+ hwState.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+
+ mProcessing = true;
+ mGestureInterpreter->PushHardwareState(&hwState);
+ mProcessing = false;
+
+ std::list<NotifyArgs> out = processGestures(when, readTime);
+
+ mMotionAccumulator.finishSync();
+ mMscTimestamp = 0;
+ return out;
+}
+
+void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
+ ALOGD("Gesture ready: %s", gesture->String().c_str());
+ if (!mProcessing) {
+ ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
+ return;
+ }
+ mGesturesToProcess.push_back(*gesture);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out = {};
+ for (Gesture& gesture : mGesturesToProcess) {
+ switch (gesture.type) {
+ case kGestureTypeMove:
+ out.push_back(handleMove(when, readTime, gesture));
+ break;
+ case kGestureTypeButtonsChange:
+ out += handleButtonsChange(when, readTime, gesture);
+ break;
+ default:
+ // TODO(b/251196347): handle more gesture types.
+ break;
+ }
+ }
+ mGesturesToProcess.clear();
+ return out;
+}
+
+NotifyArgs TouchpadInputMapper::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &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, gesture.details.move.dx);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
+ 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, &props, &coords, xCursorPosition, yCursorPosition);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ std::list<NotifyArgs> out = {};
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &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);
+ const uint32_t buttonsPressed = gesture.details.buttons.down;
+ bool pointerDown = isPointerDown(mButtonState) ||
+ buttonsPressed &
+ (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+
+ uint32_t newButtonState = mButtonState;
+ std::list<NotifyArgs> pressEvents = {};
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsPressed & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState |= actionButton;
+ pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, newButtonState,
+ /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ }
+ if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
+ mDownTime = when;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ out.splice(out.end(), pressEvents);
+
+ // The same button may be in both down and up in the same gesture, in which case we should treat
+ // it as having gone down and then up. So, we treat a single button change gesture as two state
+ // changes: a set of buttons going down, followed by a set of buttons going up.
+ mButtonState = newButtonState;
+
+ const uint32_t buttonsReleased = gesture.details.buttons.up;
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsReleased & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState &= ~actionButton;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ actionButton, newButtonState, /* pointerCount= */ 1,
+ &props, &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, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ mButtonState = newButtonState;
+ return out;
+}
+
+NotifyMotionArgs TouchpadInputMapper::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) {
+ // TODO(b/260226362): consider what the appropriate source for these events is.
+ const uint32_t source = AINPUT_SOURCE_MOUSE;
+
+ return NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), source,
+ mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
+ /* actionButton= */ actionButton, /* flags= */ 0,
+ getContext()->getGlobalMetaState(), buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties, pointerCoords,
+ /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
+ yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
new file mode 100644
index 0000000..fe6b1fe
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -0,0 +1,78 @@
+/*
+ * 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 <memory>
+
+#include <PointerControllerInterface.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputMapper.h"
+#include "NotifyArgs.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+class TouchpadInputMapper : public InputMapper {
+public:
+ explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
+ ~TouchpadInputMapper();
+
+ uint32_t getSources() const override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+
+ void consumeGesture(const Gesture* gesture);
+
+private:
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
+ NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+
+ 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);
+
+ std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
+ mGestureInterpreter;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
+
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ MultiTouchMotionAccumulator mMotionAccumulator;
+ TouchButtonAccumulator mTouchButtonAccumulator;
+ int32_t mMscTimestamp = 0;
+
+ bool mProcessing = false;
+ std::vector<Gesture> mGesturesToProcess;
+
+ // The current button state according to the gestures library, but converted into MotionEvent
+ // button values (AMOTION_EVENT_BUTTON_...).
+ uint32_t mButtonState = 0;
+ nsecs_t mDownTime = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 33db527..7645b12 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -35,16 +35,18 @@
info->setVibrator(true);
}
-void VibratorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) {
// TODO: Handle FF_STATUS, although it does not seem to be widely supported.
+ return {};
}
-void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
- int32_t token) {
+std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence,
+ ssize_t repeat, int32_t token) {
if (DEBUG_VIBRATOR) {
ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
sequence.toString().c_str(), repeat, token);
}
+ std::list<NotifyArgs> out;
mVibrating = true;
mSequence = sequence;
@@ -53,19 +55,22 @@
mIndex = -1;
// Request InputReader to notify InputManagerService for vibration started.
- NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
- getListener().notifyVibratorState(&args);
- nextStep();
+ out.push_back(
+ NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), true));
+ out += nextStep();
+ return out;
}
-void VibratorInputMapper::cancelVibrate(int32_t token) {
+std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) {
if (DEBUG_VIBRATOR) {
ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
}
+ std::list<NotifyArgs> out;
if (mVibrating && mToken == token) {
- stopVibrating();
+ out.push_back(stopVibrating());
}
+ return out;
}
bool VibratorInputMapper::isVibrating() {
@@ -76,26 +81,29 @@
return getDeviceContext().getVibratorIds();
}
-void VibratorInputMapper::timeoutExpired(nsecs_t when) {
+std::list<NotifyArgs> VibratorInputMapper::timeoutExpired(nsecs_t when) {
+ std::list<NotifyArgs> out;
if (mVibrating) {
if (when >= mNextStepTime) {
- nextStep();
+ out += nextStep();
} else {
getContext()->requestTimeoutAtTime(mNextStepTime);
}
}
+ return out;
}
-void VibratorInputMapper::nextStep() {
+std::list<NotifyArgs> VibratorInputMapper::nextStep() {
if (DEBUG_VIBRATOR) {
ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
}
+ std::list<NotifyArgs> out;
mIndex += 1;
if (size_t(mIndex) >= mSequence.pattern.size()) {
if (mRepeat < 0) {
// We are done.
- stopVibrating();
- return;
+ out.push_back(stopVibrating());
+ return out;
}
mIndex = mRepeat;
}
@@ -122,9 +130,10 @@
if (DEBUG_VIBRATOR) {
ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
}
+ return out;
}
-void VibratorInputMapper::stopVibrating() {
+NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() {
mVibrating = false;
if (DEBUG_VIBRATOR) {
ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
@@ -132,8 +141,7 @@
getDeviceContext().cancelVibrate();
// Request InputReader to notify InputManagerService for vibration complete.
- NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
- getListener().notifyVibratorState(&args);
+ return NotifyVibratorStateArgs(getContext()->getNextId(), systemTime(), getDeviceId(), false);
}
void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 894c573..e98f63a 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -27,13 +27,14 @@
virtual uint32_t getSources() const override;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
- virtual void process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
- virtual void vibrate(const VibrationSequence& sequence, ssize_t repeat, int32_t token) override;
- virtual void cancelVibrate(int32_t token) override;
+ [[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) override;
+ [[nodiscard]] std::list<NotifyArgs> cancelVibrate(int32_t token) override;
virtual bool isVibrating() override;
virtual std::vector<int32_t> getVibratorIds() override;
- virtual void timeoutExpired(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
virtual void dump(std::string& dump) override;
private:
@@ -44,8 +45,8 @@
ssize_t mIndex;
nsecs_t mNextStepTime;
- void nextStep();
- void stopVibrating();
+ [[nodiscard]] std::list<NotifyArgs> nextStep();
+ [[nodiscard]] NotifyVibratorStateArgs stopVibrating();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index ed4c789..1380604 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -32,6 +32,14 @@
void process(const RawEvent* rawEvent);
uint32_t getButtonState() const;
+ inline bool isLeftPressed() const { return mBtnLeft; }
+ inline bool isRightPressed() const { return mBtnRight; }
+ inline bool isMiddlePressed() const { return mBtnMiddle; }
+ inline bool isBackPressed() const { return mBtnBack; }
+ inline bool isSidePressed() const { return mBtnSide; }
+ inline bool isForwardPressed() const { return mBtnForward; }
+ inline bool isExtraPressed() const { return mBtnExtra; }
+ inline bool isTaskPressed() const { return mBtnTask; }
private:
bool mBtnLeft;
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
new file mode 100644
index 0000000..2da1d81
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "HidUsageAccumulator.h"
+
+namespace android {
+
+void HidUsageAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_MSC && rawEvent.code == MSC_SCAN) {
+ mCurrentHidUsage = rawEvent.value;
+ return;
+ }
+
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ reset();
+ return;
+ }
+}
+
+int32_t HidUsageAccumulator::consumeCurrentHidUsage() {
+ const int32_t currentHidUsage = mCurrentHidUsage;
+ reset();
+ return currentHidUsage;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
new file mode 100644
index 0000000..740a710
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/HidUsageAccumulator.h
@@ -0,0 +1,40 @@
+/*
+ * 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
+
+#include "EventHub.h"
+
+#include <cstdint>
+
+namespace android {
+
+/* Keeps track of the state of currently reported HID usage code. */
+class HidUsageAccumulator {
+public:
+ explicit HidUsageAccumulator() = default;
+ inline void reset() { *this = HidUsageAccumulator(); }
+
+ void process(const RawEvent& rawEvent);
+
+ /* This must be called when processing the `EV_KEY` event. Returns 0 if invalid. */
+ int32_t consumeCurrentHidUsage();
+
+private:
+ int32_t mCurrentHidUsage{};
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
new file mode 100644
index 0000000..8746729
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+// clang-format off
+#include "../Macros.h"
+// clang-format on
+#include "MultiTouchMotionAccumulator.h"
+
+namespace android {
+
+// --- MultiTouchMotionAccumulator ---
+
+MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
+ : mCurrentSlot(-1), mUsingSlotsProtocol(false) {}
+
+void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
+ bool usingSlotsProtocol) {
+ mUsingSlotsProtocol = usingSlotsProtocol;
+ mSlots = std::vector<Slot>(slotCount);
+
+ mCurrentSlot = -1;
+ if (mUsingSlotsProtocol) {
+ // Query the driver for the current slot index and use it as the initial slot before we
+ // start reading events from the device. It is possible that the current slot index will
+ // not be the same as it was when the first event was written into the evdev buffer, which
+ // means the input mapper could start out of sync with the initial state of the events in
+ // the evdev buffer. In the extremely unlikely case that this happens, the data from two
+ // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
+ // touch point to "jump", but at least there will be no stuck touches.
+ int32_t initialSlot;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
+ }
+ }
+}
+
+void MultiTouchMotionAccumulator::resetSlots() {
+ for (Slot& slot : mSlots) {
+ slot.clear();
+ }
+ mCurrentSlot = -1;
+}
+
+void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
+ if (rawEvent->type == EV_ABS) {
+ bool newSlot = false;
+ if (mUsingSlotsProtocol) {
+ if (rawEvent->code == ABS_MT_SLOT) {
+ mCurrentSlot = rawEvent->value;
+ newSlot = true;
+ }
+ } else if (mCurrentSlot < 0) {
+ mCurrentSlot = 0;
+ }
+
+ if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
+ if (newSlot) {
+ ALOGW_IF(DEBUG_POINTERS,
+ "MultiTouch device emitted invalid slot index %d but it "
+ "should be between 0 and %zd; ignoring this slot.",
+ mCurrentSlot, mSlots.size() - 1);
+ }
+ } else {
+ Slot& slot = mSlots[mCurrentSlot];
+ // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
+ // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
+ // updating the slot.
+ if (!mUsingSlotsProtocol) {
+ slot.mInUse = true;
+ }
+
+ switch (rawEvent->code) {
+ case ABS_MT_POSITION_X:
+ slot.mAbsMtPositionX = rawEvent->value;
+ warnIfNotInUse(*rawEvent, slot);
+ break;
+ case ABS_MT_POSITION_Y:
+ slot.mAbsMtPositionY = rawEvent->value;
+ warnIfNotInUse(*rawEvent, slot);
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ slot.mAbsMtTouchMajor = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ slot.mAbsMtTouchMinor = rawEvent->value;
+ slot.mHaveAbsMtTouchMinor = true;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ slot.mAbsMtWidthMajor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ slot.mAbsMtWidthMinor = rawEvent->value;
+ slot.mHaveAbsMtWidthMinor = true;
+ break;
+ case ABS_MT_ORIENTATION:
+ slot.mAbsMtOrientation = rawEvent->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (mUsingSlotsProtocol && rawEvent->value < 0) {
+ // The slot is no longer in use but it retains its previous contents,
+ // which may be reused for subsequent touches.
+ slot.mInUse = false;
+ } else {
+ slot.mInUse = true;
+ slot.mAbsMtTrackingId = rawEvent->value;
+ }
+ break;
+ case ABS_MT_PRESSURE:
+ slot.mAbsMtPressure = rawEvent->value;
+ break;
+ case ABS_MT_DISTANCE:
+ slot.mAbsMtDistance = rawEvent->value;
+ break;
+ case ABS_MT_TOOL_TYPE:
+ slot.mAbsMtToolType = rawEvent->value;
+ slot.mHaveAbsMtToolType = true;
+ break;
+ }
+ }
+ } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
+ // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+ mCurrentSlot += 1;
+ }
+}
+
+void MultiTouchMotionAccumulator::finishSync() {
+ if (!mUsingSlotsProtocol) {
+ resetSlots();
+ }
+}
+
+void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
+ if (!slot.mInUse) {
+ ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
+ event.code, event.value, mCurrentSlot, slot.mAbsMtTrackingId);
+ }
+}
+
+// --- MultiTouchMotionAccumulator::Slot ---
+
+int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
+ if (mHaveAbsMtToolType) {
+ switch (mAbsMtToolType) {
+ case MT_TOOL_FINGER:
+ return AMOTION_EVENT_TOOL_TYPE_FINGER;
+ case MT_TOOL_PEN:
+ return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ case MT_TOOL_PALM:
+ return AMOTION_EVENT_TOOL_TYPE_PALM;
+ }
+ }
+ return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
new file mode 100644
index 0000000..62bc780
--- /dev/null
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -0,0 +1,94 @@
+/*
+ * 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
+
+#include <linux/input-event-codes.h>
+#include <stdint.h>
+#include <vector>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+
+namespace android {
+
+/* Keeps track of the state of multi-touch protocol. */
+class MultiTouchMotionAccumulator {
+public:
+ class Slot {
+ public:
+ inline bool isInUse() const { return mInUse; }
+ inline int32_t getX() const { return mAbsMtPositionX; }
+ inline int32_t getY() const { return mAbsMtPositionY; }
+ inline int32_t getTouchMajor() const { return mAbsMtTouchMajor; }
+ inline int32_t getTouchMinor() const {
+ return mHaveAbsMtTouchMinor ? mAbsMtTouchMinor : mAbsMtTouchMajor;
+ }
+ inline int32_t getToolMajor() const { return mAbsMtWidthMajor; }
+ inline int32_t getToolMinor() const {
+ return mHaveAbsMtWidthMinor ? mAbsMtWidthMinor : mAbsMtWidthMajor;
+ }
+ inline int32_t getOrientation() const { return mAbsMtOrientation; }
+ inline int32_t getTrackingId() const { return mAbsMtTrackingId; }
+ inline int32_t getPressure() const { return mAbsMtPressure; }
+ inline int32_t getDistance() const { return mAbsMtDistance; }
+ int32_t getToolType() const;
+
+ private:
+ friend class MultiTouchMotionAccumulator;
+
+ bool mInUse = false;
+ bool mHaveAbsMtTouchMinor = false;
+ bool mHaveAbsMtWidthMinor = false;
+ bool mHaveAbsMtToolType = false;
+
+ int32_t mAbsMtPositionX = 0;
+ int32_t mAbsMtPositionY = 0;
+ int32_t mAbsMtTouchMajor = 0;
+ int32_t mAbsMtTouchMinor = 0;
+ int32_t mAbsMtWidthMajor = 0;
+ int32_t mAbsMtWidthMinor = 0;
+ int32_t mAbsMtOrientation = 0;
+ int32_t mAbsMtTrackingId = -1;
+ int32_t mAbsMtPressure = 0;
+ int32_t mAbsMtDistance = 0;
+ int32_t mAbsMtToolType = 0;
+
+ void clear() { *this = Slot(); }
+ };
+
+ MultiTouchMotionAccumulator();
+
+ void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
+ void process(const RawEvent* rawEvent);
+ void finishSync();
+
+ 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);
+ return mSlots[index];
+ }
+
+private:
+ int32_t mCurrentSlot;
+ std::vector<Slot> mSlots;
+ bool mUsingSlotsProtocol;
+
+ void resetSlots();
+ void warnIfNotInUse(const RawEvent& event, const Slot& slot);
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 86153d3..6601702 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -21,55 +21,40 @@
namespace android {
-TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) {
- clearButtons();
+void TouchButtonAccumulator::configure() {
+ mHaveBtnTouch = mDeviceContext.hasScanCode(BTN_TOUCH);
+ mHaveStylus = mDeviceContext.hasScanCode(BTN_TOOL_PEN) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
+ mDeviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
}
-void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) {
- mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH);
- mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) ||
- deviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
- deviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
- deviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
- deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
-}
-
-void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) {
- mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH);
- mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS);
+void TouchButtonAccumulator::reset() {
+ mBtnTouch = mDeviceContext.isKeyPressed(BTN_TOUCH);
+ mBtnStylus = mDeviceContext.isKeyPressed(BTN_STYLUS) ||
+ mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_PRIMARY);
// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
- mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0);
- mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER);
- mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN);
- mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER);
- mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH);
- mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL);
- mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
- mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE);
- mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS);
- mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
- mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
- mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
-}
-
-void TouchButtonAccumulator::clearButtons() {
- mBtnTouch = 0;
- mBtnStylus = 0;
- mBtnStylus2 = 0;
- mBtnToolFinger = 0;
- mBtnToolPen = 0;
- mBtnToolRubber = 0;
- mBtnToolBrush = 0;
- mBtnToolPencil = 0;
- mBtnToolAirbrush = 0;
- mBtnToolMouse = 0;
- mBtnToolLens = 0;
- mBtnToolDoubleTap = 0;
- mBtnToolTripleTap = 0;
- mBtnToolQuadTap = 0;
+ mBtnStylus2 = mDeviceContext.isKeyPressed(BTN_STYLUS2) || mDeviceContext.isKeyPressed(BTN_0) ||
+ mDeviceContext.isKeyCodePressed(AKEYCODE_STYLUS_BUTTON_SECONDARY);
+ mBtnToolFinger = mDeviceContext.isKeyPressed(BTN_TOOL_FINGER);
+ mBtnToolPen = mDeviceContext.isKeyPressed(BTN_TOOL_PEN);
+ mBtnToolRubber = mDeviceContext.isKeyPressed(BTN_TOOL_RUBBER);
+ mBtnToolBrush = mDeviceContext.isKeyPressed(BTN_TOOL_BRUSH);
+ mBtnToolPencil = mDeviceContext.isKeyPressed(BTN_TOOL_PENCIL);
+ mBtnToolAirbrush = mDeviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
+ mBtnToolMouse = mDeviceContext.isKeyPressed(BTN_TOOL_MOUSE);
+ mBtnToolLens = mDeviceContext.isKeyPressed(BTN_TOOL_LENS);
+ mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
+ mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
+ mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+ mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP);
+ mHidUsageAccumulator.reset();
}
void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
+ mHidUsageAccumulator.process(*rawEvent);
+
if (rawEvent->type == EV_KEY) {
switch (rawEvent->code) {
case BTN_TOUCH:
@@ -116,7 +101,32 @@
case BTN_TOOL_QUADTAP:
mBtnToolQuadTap = rawEvent->value;
break;
+ case BTN_TOOL_QUINTTAP:
+ mBtnToolQuintTap = rawEvent->value;
+ break;
+ default:
+ processMappedKey(rawEvent->code, rawEvent->value);
}
+ return;
+ }
+}
+
+void TouchButtonAccumulator::processMappedKey(int32_t scanCode, bool down) {
+ int32_t keyCode, metaState;
+ uint32_t flags;
+ if (mDeviceContext.mapKey(scanCode, mHidUsageAccumulator.consumeCurrentHidUsage(),
+ 0 /*metaState*/, &keyCode, &metaState, &flags) != OK) {
+ return;
+ }
+ switch (keyCode) {
+ case AKEYCODE_STYLUS_BUTTON_PRIMARY:
+ mBtnStylus = down;
+ break;
+ case AKEYCODE_STYLUS_BUTTON_SECONDARY:
+ mBtnStylus2 = down;
+ break;
+ default:
+ break;
}
}
@@ -141,7 +151,8 @@
if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
return AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+ if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap ||
+ mBtnToolQuintTap) {
return AMOTION_EVENT_TOOL_TYPE_FINGER;
}
return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -150,7 +161,7 @@
bool TouchButtonAccumulator::isToolActive() const {
return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
- mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+ mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap;
}
bool TouchButtonAccumulator::isHovering() const {
@@ -161,4 +172,19 @@
return mHaveStylus;
}
+bool TouchButtonAccumulator::hasButtonTouch() const {
+ return mHaveBtnTouch;
+}
+
+int TouchButtonAccumulator::getTouchCount() const {
+ if (mBtnTouch) {
+ if (mBtnToolQuintTap) return 5;
+ if (mBtnToolQuadTap) return 4;
+ if (mBtnToolTripleTap) return 3;
+ if (mBtnToolDoubleTap) return 2;
+ if (mBtnToolFinger) return 1;
+ }
+ return 0;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 7b889be..2e70e2e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -16,7 +16,8 @@
#pragma once
-#include <stdint.h>
+#include <cstdint>
+#include "HidUsageAccumulator.h"
namespace android {
@@ -26,9 +27,11 @@
/* Keeps track of the state of touch, stylus and tool buttons. */
class TouchButtonAccumulator {
public:
- TouchButtonAccumulator();
- void configure(InputDeviceContext& deviceContext);
- void reset(InputDeviceContext& deviceContext);
+ explicit TouchButtonAccumulator(InputDeviceContext& deviceContext)
+ : mDeviceContext(deviceContext){};
+
+ void configure();
+ void reset();
void process(const RawEvent* rawEvent);
@@ -37,27 +40,34 @@
bool isToolActive() const;
bool isHovering() const;
bool hasStylus() const;
+ bool hasButtonTouch() const;
+ int getTouchCount() const;
private:
- bool mHaveBtnTouch;
- bool mHaveStylus;
+ bool mHaveBtnTouch{};
+ bool mHaveStylus{};
- bool mBtnTouch;
- bool mBtnStylus;
- bool mBtnStylus2;
- bool mBtnToolFinger;
- bool mBtnToolPen;
- bool mBtnToolRubber;
- bool mBtnToolBrush;
- bool mBtnToolPencil;
- bool mBtnToolAirbrush;
- bool mBtnToolMouse;
- bool mBtnToolLens;
- bool mBtnToolDoubleTap;
- bool mBtnToolTripleTap;
- bool mBtnToolQuadTap;
+ bool mBtnTouch{};
+ bool mBtnStylus{};
+ bool mBtnStylus2{};
+ bool mBtnToolFinger{};
+ bool mBtnToolPen{};
+ bool mBtnToolRubber{};
+ bool mBtnToolBrush{};
+ bool mBtnToolPencil{};
+ bool mBtnToolAirbrush{};
+ bool mBtnToolMouse{};
+ bool mBtnToolLens{};
+ bool mBtnToolDoubleTap{};
+ bool mBtnToolTripleTap{};
+ bool mBtnToolQuadTap{};
+ bool mBtnToolQuintTap{};
- void clearButtons();
+ HidUsageAccumulator mHidUsageAccumulator{};
+
+ InputDeviceContext& mDeviceContext;
+
+ void processMappedKey(int32_t scanCode, bool down);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
new file mode 100644
index 0000000..81b4968
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG "Gestures"
+#endif
+
+#include <stdio.h>
+
+#include <log/log.h>
+
+#include "include/gestures.h"
+
+extern "C" {
+
+void gestures_log(int verb, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ if (verb == GESTURES_LOG_ERROR) {
+ LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
+ } else if (verb == GESTURES_LOG_INFO) {
+ LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
+ } else {
+ LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+ }
+ va_end(args);
+}
+}
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index 7430731..693ff06 100644
--- a/services/inputflinger/reporter/Android.bp
+++ b/services/inputflinger/reporter/Android.bp
@@ -23,13 +23,14 @@
cc_library_headers {
name: "libinputreporter_headers",
+ host_supported: true,
export_include_dirs: ["."],
}
filegroup {
name: "libinputreporter_sources",
srcs: [
- "InputReporter.cpp",
+ "InputReporter.cpp",
],
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 76500c5..53d821f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -23,6 +23,7 @@
cc_test {
name: "inputflinger_tests",
+ host_supported: true,
defaults: [
"inputflinger_defaults",
// For all targets inside inputflinger, these tests build all of their sources using their
@@ -39,14 +40,18 @@
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
+ "FakeEventHub.cpp",
+ "FakeInputReaderPolicy.cpp",
+ "FakePointerController.cpp",
"FocusResolver_test.cpp",
- "IInputFlingerQuery.aidl",
+ "InputMapperTest.cpp",
"InputProcessor_test.cpp",
"InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
- "InputFlingerService_test.cpp",
+ "InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
+ "NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
"TestInputListener.cpp",
"UinputDevice.cpp",
@@ -58,10 +63,33 @@
"frameworks/native/libs/input",
],
},
+ target: {
+ android: {
+ shared_libs: [
+ "libinput",
+ "libvintf",
+ ],
+ },
+ host: {
+ include_dirs: [
+ "bionic/libc/kernel/android/uapi/",
+ "bionic/libc/kernel/uapi",
+ ],
+ cflags: [
+ "-D__ANDROID_HOST__",
+ ],
+ static_libs: [
+ "libinput",
+ ],
+ },
+ },
static_libs: [
"libc++fs",
"libgmock",
],
require_root: true,
+ test_options: {
+ unit_test: true,
+ },
test_suites: ["device-tests"],
}
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
index f8be48a..25adeea 100644
--- a/services/inputflinger/tests/AnrTracker_test.cpp
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -137,7 +137,7 @@
ASSERT_TRUE(tracker.empty());
- ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+ ASSERT_EQ(LLONG_MAX, tracker.firstTimeout());
// Can't call firstToken() if tracker.empty()
}
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 9380c71..2e296da 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -68,12 +68,18 @@
int32_t mDeviceId;
virtual void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "It's only possible to interact with uinput on device";
+#endif
mEventHub = std::make_unique<EventHub>();
consumeInitialDeviceAddedEvents();
mKeyboard = createUinputDevice<UinputHomeKey>();
ASSERT_NO_FATAL_FAILURE(mDeviceId = waitForDeviceCreation());
}
virtual void TearDown() override {
+#if !defined(__ANDROID__)
+ return;
+#endif
mKeyboard.reset();
waitForDeviceClose(mDeviceId);
assertNoMoreEvents();
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
new file mode 100644
index 0000000..289a780
--- /dev/null
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -0,0 +1,596 @@
+/*
+ * 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 "FakeEventHub.h"
+
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "TestConstants.h"
+
+namespace android {
+
+const std::string FakeEventHub::BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
+
+FakeEventHub::~FakeEventHub() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+}
+
+void FakeEventHub::addDevice(int32_t deviceId, const std::string& name,
+ ftl::Flags<InputDeviceClass> classes, int bus) {
+ Device* device = new Device(classes);
+ device->identifier.name = name;
+ device->identifier.bus = bus;
+ mDevices.add(deviceId, device);
+
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+}
+
+void FakeEventHub::removeDevice(int32_t deviceId) {
+ delete mDevices.valueFor(deviceId);
+ mDevices.removeItem(deviceId);
+
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+}
+
+bool FakeEventHub::isDeviceEnabled(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return false;
+ }
+ return device->enabled;
+}
+
+status_t FakeEventHub::enableDevice(int32_t deviceId) {
+ status_t result;
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
+ return OK;
+ }
+ result = device->enable();
+ return result;
+}
+
+status_t FakeEventHub::disableDevice(int32_t deviceId) {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (!device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
+ return OK;
+ }
+ return device->disable();
+}
+
+void FakeEventHub::finishDeviceScan() {
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+}
+
+void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
+ getDevice(deviceId)->configuration.addProperty(key, value);
+}
+
+void FakeEventHub::addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
+ getDevice(deviceId)->configuration.addAll(configuration);
+}
+
+void FakeEventHub::addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue,
+ int flat, int fuzz, int resolution) {
+ Device* device = getDevice(deviceId);
+
+ RawAbsoluteAxisInfo info;
+ info.valid = true;
+ info.minValue = minValue;
+ info.maxValue = maxValue;
+ info.flat = flat;
+ info.fuzz = fuzz;
+ info.resolution = resolution;
+ device->absoluteAxes.add(axis, info);
+}
+
+void FakeEventHub::addRelativeAxis(int32_t deviceId, int32_t axis) {
+ getDevice(deviceId)->relativeAxes.add(axis, true);
+}
+
+void FakeEventHub::setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
+ getDevice(deviceId)->keyCodeStates.replaceValueFor(keyCode, state);
+}
+
+void FakeEventHub::setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
+ getDevice(deviceId)->countryCode = countryCode;
+}
+
+void FakeEventHub::setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
+ getDevice(deviceId)->scanCodeStates.replaceValueFor(scanCode, state);
+}
+
+void FakeEventHub::setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
+ getDevice(deviceId)->switchStates.replaceValueFor(switchCode, state);
+}
+
+void FakeEventHub::setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
+ getDevice(deviceId)->absoluteAxisValue.replaceValueFor(axis, value);
+}
+
+void FakeEventHub::addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+ uint32_t flags) {
+ Device* device = getDevice(deviceId);
+ KeyInfo info;
+ info.keyCode = keyCode;
+ info.flags = flags;
+ if (scanCode) {
+ device->keysByScanCode.add(scanCode, info);
+ }
+ if (usageCode) {
+ device->keysByUsageCode.add(usageCode, info);
+ }
+}
+
+void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
+ getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
+void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ Device* device = getDevice(deviceId);
+ device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
+void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
+ getDevice(deviceId)->leds.add(led, initialState);
+}
+
+void FakeEventHub::addSensorAxis(int32_t deviceId, int32_t absCode,
+ InputDeviceSensorType sensorType, int32_t sensorDataIndex) {
+ SensorInfo info;
+ info.sensorType = sensorType;
+ info.sensorDataIndex = sensorDataIndex;
+ getDevice(deviceId)->sensorsByAbsCode.emplace(absCode, info);
+}
+
+void FakeEventHub::setMscEvent(int32_t deviceId, int32_t mscEvent) {
+ typename BitArray<MSC_MAX>::Buffer buffer;
+ buffer[mscEvent / 32] = 1 << mscEvent % 32;
+ getDevice(deviceId)->mscBitmask.loadFromBuffer(buffer);
+}
+
+void FakeEventHub::addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
+ mRawLightInfos.emplace(rawId, std::move(info));
+}
+
+void FakeEventHub::fakeLightBrightness(int32_t rawId, int32_t brightness) {
+ mLightBrightness.emplace(rawId, brightness);
+}
+
+void FakeEventHub::fakeLightIntensities(int32_t rawId,
+ const std::unordered_map<LightColor, int32_t> intensities) {
+ mLightIntensities.emplace(rawId, std::move(intensities));
+}
+
+bool FakeEventHub::getLedState(int32_t deviceId, int32_t led) {
+ return getDevice(deviceId)->leds.valueFor(led);
+}
+
+std::vector<std::string>& FakeEventHub::getExcludedDevices() {
+ return mExcludedDevices;
+}
+
+void FakeEventHub::addVirtualKeyDefinition(int32_t deviceId,
+ const VirtualKeyDefinition& definition) {
+ getDevice(deviceId)->virtualKeys.push_back(definition);
+}
+
+void FakeEventHub::enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type,
+ int32_t code, int32_t value) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = deviceId;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ mEvents.push_back(event);
+
+ if (type == EV_ABS) {
+ setAbsoluteAxisValue(deviceId, code, value);
+ }
+}
+
+void FakeEventHub::setVideoFrames(
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames) {
+ mVideoFrames = std::move(videoFrames);
+}
+
+void FakeEventHub::assertQueueIsEmpty() {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ const bool queueIsEmpty =
+ mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
+ [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
+ if (!queueIsEmpty) {
+ FAIL() << "Timed out waiting for EventHub queue to be emptied.";
+ }
+}
+
+FakeEventHub::Device* FakeEventHub::getDevice(int32_t deviceId) const {
+ ssize_t index = mDevices.indexOfKey(deviceId);
+ return index >= 0 ? mDevices.valueAt(index) : nullptr;
+}
+
+ftl::Flags<InputDeviceClass> FakeEventHub::getDeviceClasses(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->classes : ftl::Flags<InputDeviceClass>(0);
+}
+
+InputDeviceIdentifier FakeEventHub::getDeviceIdentifier(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->identifier : InputDeviceIdentifier();
+}
+
+int32_t FakeEventHub::getDeviceControllerNumber(int32_t) const {
+ return 0;
+}
+
+void FakeEventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ *outConfiguration = device->configuration;
+ }
+}
+
+status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->absoluteAxes.indexOfKey(axis);
+ if (index >= 0) {
+ *outAxisInfo = device->absoluteAxes.valueAt(index);
+ return OK;
+ }
+ }
+ outAxisInfo->clear();
+ return -1;
+}
+
+bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return device->relativeAxes.indexOfKey(axis) >= 0;
+ }
+ return false;
+}
+
+bool FakeEventHub::hasInputProperty(int32_t, int) const {
+ return false;
+}
+
+bool FakeEventHub::hasMscEvent(int32_t deviceId, int mscEvent) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
+ }
+ return false;
+}
+
+status_t FakeEventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+ int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ const KeyInfo* key = getKey(device, scanCode, usageCode);
+ if (key) {
+ if (outKeycode) {
+ auto it = device->keyRemapping.find(key->keyCode);
+ *outKeycode = it != device->keyRemapping.end() ? it->second : key->keyCode;
+ }
+ if (outFlags) {
+ *outFlags = key->flags;
+ }
+ if (outMetaState) {
+ *outMetaState = metaState;
+ }
+ return OK;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
+const FakeEventHub::KeyInfo* FakeEventHub::getKey(Device* device, int32_t scanCode,
+ int32_t usageCode) const {
+ if (usageCode) {
+ ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
+ if (index >= 0) {
+ return &device->keysByUsageCode.valueAt(index);
+ }
+ }
+ if (scanCode) {
+ ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+ if (index >= 0) {
+ return &device->keysByScanCode.valueAt(index);
+ }
+ }
+ return nullptr;
+}
+
+status_t FakeEventHub::mapAxis(int32_t, int32_t, AxisInfo*) const {
+ return NAME_NOT_FOUND;
+}
+
+base::Result<std::pair<InputDeviceSensorType, int32_t>> FakeEventHub::mapSensor(
+ int32_t deviceId, int32_t absCode) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return Errorf("Sensor device not found.");
+ }
+ auto it = device->sensorsByAbsCode.find(absCode);
+ if (it == device->sensorsByAbsCode.end()) {
+ return Errorf("Sensor map not found.");
+ }
+ const SensorInfo& info = it->second;
+ return std::make_pair(info.sensorType, info.sensorDataIndex);
+}
+
+void FakeEventHub::setExcludedDevices(const std::vector<std::string>& devices) {
+ mExcludedDevices = devices;
+}
+
+std::vector<RawEvent> FakeEventHub::getEvents(int) {
+ std::scoped_lock lock(mLock);
+
+ std::vector<RawEvent> buffer;
+ std::swap(buffer, mEvents);
+
+ mEventsCondition.notify_all();
+ return buffer;
+}
+
+std::vector<TouchVideoFrame> FakeEventHub::getVideoFrames(int32_t deviceId) {
+ auto it = mVideoFrames.find(deviceId);
+ if (it != mVideoFrames.end()) {
+ std::vector<TouchVideoFrame> frames = std::move(it->second);
+ mVideoFrames.erase(deviceId);
+ return frames;
+ }
+ return {};
+}
+
+int32_t FakeEventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
+ if (index >= 0) {
+ return device->scanCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+InputDeviceCountryCode FakeEventHub::getCountryCode(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->countryCode : InputDeviceCountryCode::INVALID;
+}
+
+int32_t FakeEventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
+ if (index >= 0) {
+ return device->keyCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t FakeEventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->switchStates.indexOfKey(sw);
+ if (index >= 0) {
+ return device->switchStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
+ if (index >= 0) {
+ *outValue = device->absoluteAxisValue.valueAt(index);
+ return OK;
+ }
+ }
+ *outValue = 0;
+ return -1;
+}
+
+int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return AKEYCODE_UNKNOWN;
+ }
+ auto it = device->keyCodeMapping.find(locationKeyCode);
+ return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
+}
+
+// Return true if the device has non-empty key layout.
+bool FakeEventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+ uint8_t* outFlags) const {
+ Device* device = getDevice(deviceId);
+ if (!device) return false;
+
+ bool result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
+ for (size_t i = 0; i < keyCodes.size(); i++) {
+ for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
+ if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ }
+ }
+ for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+ if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+ return result;
+}
+
+bool FakeEventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+ return index >= 0;
+ }
+ return false;
+}
+
+bool FakeEventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return false;
+ }
+ for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
+ if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
+ return true;
+ }
+ }
+ for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+ if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FakeEventHub::hasLed(int32_t deviceId, int32_t led) const {
+ Device* device = getDevice(deviceId);
+ return device && device->leds.indexOfKey(led) >= 0;
+}
+
+void FakeEventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->leds.indexOfKey(led);
+ if (index >= 0) {
+ device->leds.replaceValueAt(led, on);
+ } else {
+ ADD_FAILURE() << "Attempted to set the state of an LED that the EventHub declared "
+ "was not present. led="
+ << led;
+ }
+ }
+}
+
+void FakeEventHub::getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ outVirtualKeys.clear();
+
+ Device* device = getDevice(deviceId);
+ if (device) {
+ outVirtualKeys = device->virtualKeys;
+ }
+}
+
+const std::shared_ptr<KeyCharacterMap> FakeEventHub::getKeyCharacterMap(int32_t) const {
+ return nullptr;
+}
+
+bool FakeEventHub::setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) {
+ return false;
+}
+
+std::vector<int32_t> FakeEventHub::getVibratorIds(int32_t deviceId) const {
+ return mVibrators;
+}
+
+std::optional<int32_t> FakeEventHub::getBatteryCapacity(int32_t, int32_t) const {
+ return BATTERY_CAPACITY;
+}
+
+std::optional<int32_t> FakeEventHub::getBatteryStatus(int32_t, int32_t) const {
+ return BATTERY_STATUS;
+}
+
+std::vector<int32_t> FakeEventHub::getRawBatteryIds(int32_t deviceId) const {
+ return {DEFAULT_BATTERY};
+}
+
+std::optional<RawBatteryInfo> FakeEventHub::getRawBatteryInfo(int32_t deviceId,
+ int32_t batteryId) const {
+ if (batteryId != DEFAULT_BATTERY) return {};
+ static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
+ .name = "default battery",
+ .flags = InputBatteryClass::CAPACITY,
+ .path = BATTERY_DEVPATH};
+ return BATTERY_INFO;
+}
+
+std::vector<int32_t> FakeEventHub::getRawLightIds(int32_t deviceId) const {
+ std::vector<int32_t> ids;
+ for (const auto& [rawId, info] : mRawLightInfos) {
+ ids.push_back(rawId);
+ }
+ return ids;
+}
+
+std::optional<RawLightInfo> FakeEventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) const {
+ auto it = mRawLightInfos.find(lightId);
+ if (it == mRawLightInfos.end()) {
+ return std::nullopt;
+ }
+ return it->second;
+}
+
+void FakeEventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
+ mLightBrightness.emplace(lightId, brightness);
+}
+
+void FakeEventHub::setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) {
+ mLightIntensities.emplace(lightId, intensities);
+};
+
+std::optional<int32_t> FakeEventHub::getLightBrightness(int32_t deviceId, int32_t lightId) const {
+ auto lightIt = mLightBrightness.find(lightId);
+ if (lightIt == mLightBrightness.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+}
+
+std::optional<std::unordered_map<LightColor, int32_t>> FakeEventHub::getLightIntensities(
+ int32_t deviceId, int32_t lightId) const {
+ auto lightIt = mLightIntensities.find(lightId);
+ if (lightIt == mLightIntensities.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
new file mode 100644
index 0000000..fb3c859
--- /dev/null
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -0,0 +1,226 @@
+/*
+ * 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 <condition_variable>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <EventHub.h>
+#include <InputDevice.h>
+#include <ftl/flags.h>
+#include <input/PropertyMap.h>
+#include <input/VirtualKeyMap.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
+using android::hardware::input::InputDeviceCountryCode;
+
+namespace android {
+
+class FakeEventHub : public EventHubInterface {
+ struct KeyInfo {
+ int32_t keyCode;
+ uint32_t flags;
+ };
+
+ struct SensorInfo {
+ InputDeviceSensorType sensorType;
+ int32_t sensorDataIndex;
+ };
+
+ struct Device {
+ InputDeviceIdentifier identifier;
+ ftl::Flags<InputDeviceClass> classes;
+ PropertyMap configuration;
+ KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
+ KeyedVector<int, bool> relativeAxes;
+ KeyedVector<int32_t, int32_t> keyCodeStates;
+ KeyedVector<int32_t, int32_t> scanCodeStates;
+ KeyedVector<int32_t, int32_t> switchStates;
+ KeyedVector<int32_t, int32_t> absoluteAxisValue;
+ KeyedVector<int32_t, KeyInfo> keysByScanCode;
+ KeyedVector<int32_t, KeyInfo> keysByUsageCode;
+ std::unordered_map<int32_t, int32_t> keyRemapping;
+ KeyedVector<int32_t, bool> leds;
+ // fake mapping which would normally come from keyCharacterMap
+ std::unordered_map<int32_t, int32_t> keyCodeMapping;
+ std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
+ BitArray<MSC_MAX> mscBitmask;
+ std::vector<VirtualKeyDefinition> virtualKeys;
+ bool enabled;
+ InputDeviceCountryCode countryCode;
+
+ status_t enable() {
+ enabled = true;
+ return OK;
+ }
+
+ status_t disable() {
+ enabled = false;
+ return OK;
+ }
+
+ explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
+ };
+
+ std::mutex mLock;
+ std::condition_variable mEventsCondition;
+
+ KeyedVector<int32_t, Device*> mDevices;
+ std::vector<std::string> mExcludedDevices;
+ std::vector<RawEvent> mEvents GUARDED_BY(mLock);
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
+ std::vector<int32_t> mVibrators = {0, 1};
+ std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
+ // Simulates a device light brightness, from light id to light brightness.
+ std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
+ // Simulates a device light intensities, from light id to light intensities map.
+ std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
+ mLightIntensities;
+
+public:
+ static constexpr int32_t DEFAULT_BATTERY = 1;
+ static constexpr int32_t BATTERY_STATUS = 4;
+ static constexpr int32_t BATTERY_CAPACITY = 66;
+ static const std::string BATTERY_DEVPATH;
+
+ virtual ~FakeEventHub();
+ FakeEventHub() {}
+
+ void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes,
+ int bus = 0);
+ void removeDevice(int32_t deviceId);
+
+ bool isDeviceEnabled(int32_t deviceId) const override;
+ status_t enableDevice(int32_t deviceId) override;
+ status_t disableDevice(int32_t deviceId) override;
+
+ void finishDeviceScan();
+
+ void addConfigurationProperty(int32_t deviceId, const char* key, const char* value);
+ void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration);
+
+ void addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat,
+ int fuzz, int resolution = 0);
+ void addRelativeAxis(int32_t deviceId, int32_t axis);
+ void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value);
+
+ void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode);
+
+ void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state);
+ void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state);
+ void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state);
+
+ void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+ uint32_t flags);
+ void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
+ void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
+
+ void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
+ int32_t sensorDataIndex);
+
+ void setMscEvent(int32_t deviceId, int32_t mscEvent);
+
+ void addLed(int32_t deviceId, int32_t led, bool initialState);
+ void addRawLightInfo(int32_t rawId, RawLightInfo&& info);
+ void fakeLightBrightness(int32_t rawId, int32_t brightness);
+ void fakeLightIntensities(int32_t rawId,
+ const std::unordered_map<LightColor, int32_t> intensities);
+ bool getLedState(int32_t deviceId, int32_t led);
+
+ std::vector<std::string>& getExcludedDevices();
+
+ void setVideoFrames(
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames);
+
+ void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
+ int32_t value);
+ void assertQueueIsEmpty();
+
+private:
+ Device* getDevice(int32_t deviceId) const;
+
+ ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override;
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
+ int32_t getDeviceControllerNumber(int32_t) const override;
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+ status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const override;
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override;
+ bool hasInputProperty(int32_t, int) const override;
+ bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+ status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override;
+ const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const;
+
+ status_t mapAxis(int32_t, int32_t, AxisInfo*) const override;
+ base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+ int32_t deviceId, int32_t absCode) const override;
+ void setExcludedDevices(const std::vector<std::string>& devices) override;
+ std::vector<RawEvent> getEvents(int) override;
+ std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+ InputDeviceCountryCode getCountryCode(int32_t deviceId) const override;
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+ status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override;
+ int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
+
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+ uint8_t* outFlags) const override;
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+ bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override;
+ bool hasLed(int32_t deviceId, int32_t led) const override;
+ void setLedState(int32_t deviceId, int32_t led, bool on) override;
+ void getVirtualKeyDefinitions(int32_t deviceId,
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override;
+ bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override;
+
+ void vibrate(int32_t, const VibrationElement&) override {}
+ void cancelVibrate(int32_t) override {}
+ std::vector<int32_t> getVibratorIds(int32_t deviceId) const override;
+
+ std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override;
+ std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override;
+ std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override;
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+ int32_t batteryId) const override;
+
+ std::vector<int32_t> getRawLightIds(int32_t deviceId) const override;
+ std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override;
+ void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override;
+ void setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) override;
+ std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override;
+ std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t deviceId, int32_t lightId) const override;
+
+ void dump(std::string&) const override {}
+ void monitor() const override {}
+ void requestReopenDevices() override {}
+ void wake() override {}
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
new file mode 100644
index 0000000..3af4298
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 "FakeInputReaderPolicy.h"
+
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+
+#include "TestConstants.h"
+#include "ui/Rotation.h"
+
+namespace android {
+
+void FakeInputReaderPolicy::assertInputDevicesChanged() {
+ waitForInputDevices([](bool devicesChanged) {
+ if (!devicesChanged) {
+ FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+ }
+ });
+}
+
+void FakeInputReaderPolicy::assertInputDevicesNotChanged() {
+ waitForInputDevices([](bool devicesChanged) {
+ if (devicesChanged) {
+ FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+ }
+ });
+}
+
+void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mStylusGestureNotified);
+ ASSERT_EQ(deviceId, *mStylusGestureNotified);
+ mStylusGestureNotified.reset();
+}
+
+void FakeInputReaderPolicy::assertStylusGestureNotNotified() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mStylusGestureNotified);
+}
+
+void FakeInputReaderPolicy::clearViewports() {
+ mViewports.clear();
+ mConfig.setDisplayViewports(mViewports);
+}
+
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByUniqueId(
+ const std::string& uniqueId) const {
+ return mConfig.getDisplayViewportByUniqueId(uniqueId);
+}
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByType(
+ ViewportType type) const {
+ return mConfig.getDisplayViewportByType(type);
+}
+
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByPort(
+ uint8_t displayPort) const {
+ return mConfig.getDisplayViewportByPort(displayPort);
+}
+
+void FakeInputReaderPolicy::addDisplayViewport(DisplayViewport viewport) {
+ mViewports.push_back(std::move(viewport));
+ mConfig.setDisplayViewports(mViewports);
+}
+
+void FakeInputReaderPolicy::addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType type) {
+ const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+
+ addDisplayViewport(v);
+}
+
+bool FakeInputReaderPolicy::updateViewport(const DisplayViewport& viewport) {
+ size_t count = mViewports.size();
+ for (size_t i = 0; i < count; i++) {
+ const DisplayViewport& currentViewport = mViewports[i];
+ if (currentViewport.displayId == viewport.displayId) {
+ mViewports[i] = viewport;
+ mConfig.setDisplayViewports(mViewports);
+ return true;
+ }
+ }
+ // no viewport found.
+ return false;
+}
+
+void FakeInputReaderPolicy::addExcludedDeviceName(const std::string& deviceName) {
+ mConfig.excludedDeviceNames.push_back(deviceName);
+}
+
+void FakeInputReaderPolicy::addInputPortAssociation(const std::string& inputPort,
+ uint8_t displayPort) {
+ mConfig.portAssociations.insert({inputPort, displayPort});
+}
+
+void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId) {
+ mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+}
+
+void FakeInputReaderPolicy::addDisabledDevice(int32_t deviceId) {
+ mConfig.disabledDevices.insert(deviceId);
+}
+
+void FakeInputReaderPolicy::removeDisabledDevice(int32_t deviceId) {
+ mConfig.disabledDevices.erase(deviceId);
+}
+
+void FakeInputReaderPolicy::setPointerController(
+ std::shared_ptr<FakePointerController> controller) {
+ mPointerController = std::move(controller);
+}
+
+const InputReaderConfiguration* FakeInputReaderPolicy::getReaderConfiguration() const {
+ return &mConfig;
+}
+
+const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const {
+ return mInputDevices;
+}
+
+TouchAffineTransformation FakeInputReaderPolicy::getTouchAffineTransformation(
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
+ return transform;
+}
+
+void FakeInputReaderPolicy::setTouchAffineTransformation(const TouchAffineTransformation t) {
+ transform = t;
+}
+
+PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) {
+ mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+ return mConfig.pointerCaptureRequest;
+}
+
+void FakeInputReaderPolicy::setShowTouches(bool enabled) {
+ mConfig.showTouches = enabled;
+}
+
+void FakeInputReaderPolicy::setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+ mConfig.defaultPointerDisplayId = pointerDisplayId;
+}
+
+void FakeInputReaderPolicy::setPointerGestureEnabled(bool enabled) {
+ mConfig.pointerGesturesEnabled = enabled;
+}
+
+float FakeInputReaderPolicy::getPointerGestureMovementSpeedRatio() {
+ return mConfig.pointerGestureMovementSpeedRatio;
+}
+
+float FakeInputReaderPolicy::getPointerGestureZoomSpeedRatio() {
+ return mConfig.pointerGestureZoomSpeedRatio;
+}
+
+void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) {
+ mConfig.pointerVelocityControlParameters = params;
+ mConfig.wheelVelocityControlParameters = params;
+}
+
+void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
+ *outConfig = mConfig;
+}
+
+std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointerController(
+ int32_t /*deviceId*/) {
+ return mPointerController;
+}
+
+void FakeInputReaderPolicy::notifyInputDevicesChanged(
+ const std::vector<InputDeviceInfo>& inputDevices) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mInputDevices = inputDevices;
+ mInputDevicesChanged = true;
+ mDevicesChangedCondition.notify_all();
+}
+
+std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) {
+ return nullptr;
+}
+
+std::string FakeInputReaderPolicy::getDeviceAlias(const InputDeviceIdentifier&) {
+ return "";
+}
+
+void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ const bool devicesChanged =
+ mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mInputDevicesChanged;
+ });
+ ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
+ mInputDevicesChanged = false;
+}
+
+void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mStylusGestureNotified = deviceId;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
new file mode 100644
index 0000000..c16cda4
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -0,0 +1,101 @@
+/*
+ * 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 <condition_variable>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+
+#include "FakePointerController.h"
+#include "input/DisplayViewport.h"
+#include "input/InputDevice.h"
+
+namespace android {
+
+class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+protected:
+ virtual ~FakeInputReaderPolicy() {}
+
+public:
+ FakeInputReaderPolicy() {}
+
+ void assertInputDevicesChanged();
+ void assertInputDevicesNotChanged();
+ void assertStylusGestureNotified(int32_t deviceId);
+ void assertStylusGestureNotNotified();
+
+ virtual void clearViewports();
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const;
+ void addDisplayViewport(DisplayViewport viewport);
+ void addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType type);
+ bool updateViewport(const DisplayViewport& viewport);
+ void addExcludedDeviceName(const std::string& deviceName);
+ void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort);
+ void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId);
+ void addDisabledDevice(int32_t deviceId);
+ void removeDisabledDevice(int32_t deviceId);
+ void setPointerController(std::shared_ptr<FakePointerController> controller);
+ const InputReaderConfiguration* getReaderConfiguration() const;
+ const std::vector<InputDeviceInfo>& getInputDevices() const;
+ TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+ ui::Rotation surfaceRotation);
+ void setTouchAffineTransformation(const TouchAffineTransformation t);
+ PointerCaptureRequest setPointerCapture(bool enabled);
+ void setShowTouches(bool enabled);
+ void setDefaultPointerDisplayId(int32_t pointerDisplayId);
+ void setPointerGestureEnabled(bool enabled);
+ float getPointerGestureMovementSpeedRatio();
+ float getPointerGestureZoomSpeedRatio();
+ void setVelocityControlParams(const VelocityControlParameters& params);
+
+private:
+ void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(
+ int32_t /*deviceId*/) override;
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+ std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) override;
+ std::string getDeviceAlias(const InputDeviceIdentifier&) override;
+ void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
+ void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
+
+ std::mutex mLock;
+ std::condition_variable mDevicesChangedCondition;
+
+ InputReaderConfiguration mConfig;
+ std::shared_ptr<FakePointerController> mPointerController;
+ std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+ bool mInputDevicesChanged GUARDED_BY(mLock){false};
+ std::vector<DisplayViewport> mViewports;
+ TouchAffineTransformation transform;
+ std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
+
+ uint32_t mNextPointerCaptureSequenceNumber{0};
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
new file mode 100644
index 0000000..635366b
--- /dev/null
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "FakePointerController.h"
+
+namespace android {
+
+void FakePointerController::setBounds(float minX, float minY, float maxX, float maxY) {
+ mHaveBounds = true;
+ mMinX = minX;
+ mMinY = minY;
+ mMaxX = maxX;
+ mMaxY = maxY;
+}
+
+const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() {
+ return mSpotsByDisplay;
+}
+
+void FakePointerController::setPosition(float x, float y) {
+ mX = x;
+ mY = y;
+}
+
+void FakePointerController::setButtonState(int32_t buttonState) {
+ mButtonState = buttonState;
+}
+
+int32_t FakePointerController::getButtonState() const {
+ return mButtonState;
+}
+
+void FakePointerController::getPosition(float* outX, float* outY) const {
+ *outX = mX;
+ *outY = mY;
+}
+
+int32_t FakePointerController::getDisplayId() const {
+ return mDisplayId;
+}
+
+void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
+ mDisplayId = viewport.displayId;
+}
+
+bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+ float* outMaxY) const {
+ *outMinX = mMinX;
+ *outMinY = mMinY;
+ *outMaxX = mMaxX;
+ *outMaxY = mMaxY;
+ return mHaveBounds;
+}
+
+void FakePointerController::move(float deltaX, float deltaY) {
+ mX += deltaX;
+ if (mX < mMinX) mX = mMinX;
+ if (mX > mMaxX) mX = mMaxX;
+ mY += deltaY;
+ if (mY < mMinY) mY = mMinY;
+ if (mY > mMaxY) mY = mMaxY;
+}
+
+void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+ int32_t displayId) {
+ std::vector<int32_t> newSpots;
+ // Add spots for fingers that are down.
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ newSpots.push_back(id);
+ }
+
+ mSpotsByDisplay[displayId] = newSpots;
+}
+
+void FakePointerController::clearSpots() {
+ mSpotsByDisplay.clear();
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
new file mode 100644
index 0000000..f00870f
--- /dev/null
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <PointerControllerInterface.h>
+#include <gui/constants.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <utils/BitSet.h>
+
+namespace android {
+
+class FakePointerController : public PointerControllerInterface {
+public:
+ virtual ~FakePointerController() {}
+
+ void setBounds(float minX, float minY, float maxX, float maxY);
+ const std::map<int32_t, std::vector<int32_t>>& getSpots();
+
+ void setPosition(float x, float y) override;
+ void setButtonState(int32_t buttonState) override;
+ int32_t getButtonState() const override;
+ void getPosition(float* outX, float* outY) const override;
+ int32_t getDisplayId() const override;
+ void setDisplayViewport(const DisplayViewport& viewport) override;
+
+private:
+ bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) 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,
+ int32_t displayId) override;
+ void clearSpots() override;
+
+ bool mHaveBounds{false};
+ float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
+ float mX{0}, mY{0};
+ int32_t mButtonState{0};
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+
+ std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
deleted file mode 100644
index 5aeb21f..0000000
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2020, 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.
- */
-
-import android.InputChannel;
-import android.gui.FocusRequest;
-import android.gui.WindowInfo;
-
-/** @hide */
-interface IInputFlingerQuery
-{
- /* Test interfaces */
- void getInputChannels(out InputChannel[] channels);
- void resetInputManager();
-}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 985c9c5..41c174a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <fcntl.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
#include <linux/input.h>
@@ -43,6 +44,7 @@
namespace android::inputdispatcher {
using namespace ftl::flag_operators;
+using testing::AllOf;
// An arbitrary time value.
static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -102,6 +104,39 @@
<< MotionEvent::actionToString(receivedAction);
}
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
+ bool matches = action == arg.getAction();
+ if (!matches) {
+ *result_listener << "expected action " << MotionEvent::actionToString(action)
+ << ", but got " << MotionEvent::actionToString(arg.getAction());
+ }
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "downTime should match eventTime for ACTION_DOWN events";
+ matches &= arg.getDownTime() == arg.getEventTime();
+ }
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+ matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+}
+
+MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+ return arg.getDownTime() == downTime;
+}
+
+MATCHER_P(WithSource, source, "InputEvent with specified source") {
+ *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+ << inputEventSourceToString(arg.getSource());
+ return arg.getSource() == source;
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -1186,6 +1221,7 @@
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
InputEvent* event = consume();
+ ASSERT_NE(nullptr, event);
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
@@ -1205,6 +1241,12 @@
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(int32_t expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
@@ -1892,6 +1934,64 @@
wallpaperWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ window->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setIsWallpaper(true);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ wallpaperWindow->setPreventSplitting(true);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionPointerDown(1);
+ wallpaperWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ const MotionEvent secondFingerUpEvent =
+ MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionPointerUp(1);
+ wallpaperWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
/**
* On the display, have a single window, and also an area where there's no window.
* First pointer touches the "no window" area of the screen. Second pointer touches the window.
@@ -1968,6 +2068,7 @@
mDispatcher->waitForIdle();
InputEvent* inputEvent1 = window1->consume();
+ ASSERT_NE(inputEvent1, nullptr);
window2->assertNoEvents();
MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
@@ -1977,6 +2078,7 @@
mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
mDispatcher->waitForIdle();
InputEvent* inputEvent2 = window2->consume();
+ ASSERT_NE(inputEvent2, nullptr);
MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
@@ -1986,17 +2088,13 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent3 = window2->consume();
- MotionEvent& motionEvent3 = static_cast<MotionEvent&>(*inputEvent3);
- ASSERT_EQ(motionEvent3.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// Now add new touch down on the second window
mDispatcher->notifyMotion(
&(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent4 = window2->consume();
- MotionEvent& motionEvent4 = static_cast<MotionEvent&>(*inputEvent4);
- ASSERT_EQ(motionEvent4.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
window1->consumeMotionMove();
@@ -2006,16 +2104,12 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent5 = window1->consume();
- MotionEvent& motionEvent5 = static_cast<MotionEvent&>(*inputEvent5);
- ASSERT_EQ(motionEvent5.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
mDispatcher->notifyMotion(&(
args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent6 = window1->consume();
- MotionEvent& motionEvent6 = static_cast<MotionEvent&>(*inputEvent6);
- ASSERT_EQ(motionEvent6.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
}
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -2040,10 +2134,8 @@
.x(900)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2054,12 +2146,9 @@
.x(300)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2082,8 +2171,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2095,8 +2183,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2117,12 +2204,47 @@
.x(900)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+ // No more events
+ windowLeft->assertNoEvents();
+ windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 600, 800));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+ // Send mouse cursor to the window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(100)
+ .y(100))
+ .build()));
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
}
// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
@@ -2145,8 +2267,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2169,8 +2290,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2182,8 +2302,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2203,8 +2322,102 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+}
+
+/**
+ * Inject a mouse hover event followed by a tap from touchscreen.
+ * In the current implementation, the tap does not cause a HOVER_EXIT event.
+ */
+TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
+ 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, 100, 100));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Inject a hover_move from mouse.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE,
+ ADISPLAY_ID_DEFAULT, {{50, 50}});
+ motionArgs.xCursorPosition = 50;
+ motionArgs.yCursorPosition = 50;
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE))));
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE))));
+
+ // Tap on the window
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{10, 10}});
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{10, 10}});
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+}
+
+TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowDefaultDisplay =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay",
+ ADISPLAY_ID_DEFAULT);
+ windowDefaultDisplay->setFrame(Rect(0, 0, 600, 800));
+ sp<FakeWindowHandle> windowSecondDisplay =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "SecondDisplay",
+ SECOND_DISPLAY_ID);
+ windowSecondDisplay->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
+ {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+
+ // Set cursor position in window in default display and check that hover enter and move
+ // events are generated.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(600))
+ .build()));
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+ // Remove all windows in secondary display and check that no event happens on window in
+ // primary display.
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
+ windowDefaultDisplay->assertNoEvents();
+
+ // Move cursor position in window in default display and check that only hover move
+ // event is generated and not hover enter event.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
+ {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(400)
+ .y(700))
+ .build()));
+ windowDefaultDisplay->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+ windowDefaultDisplay->assertNoEvents();
}
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
@@ -2400,6 +2613,43 @@
window->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+ // Ensure window is non-split and have some transform.
+ window->setPreventSplitting(true);
+ window->setWindowOffset(20, 40);
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(-30)
+ .y(-50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ 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
+}
+
/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
@@ -4449,6 +4699,7 @@
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
assertMotionAction(expectedAction, motionEvent.getAction());
+ ASSERT_EQ(points.size(), motionEvent.getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
@@ -7027,8 +7278,8 @@
break; // epoll_wait timed out
}
for (int i = 0; i < nFds; i++) {
- ASSERT_EQ(EPOLLIN, events[i].events);
- eventOrder.push_back(events[i].data.u64);
+ ASSERT_EQ(static_cast<uint32_t>(EPOLLIN), events[i].events);
+ eventOrder.push_back(static_cast<size_t>(events[i].data.u64));
channels[i]->consumeMotionDown();
}
}
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
deleted file mode 100644
index ca548be..0000000
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2020 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 <BnInputFlingerQuery.h>
-#include <IInputFlingerQuery.h>
-
-#include <android/os/BnInputFlinger.h>
-#include <android/os/IInputFlinger.h>
-
-#include <binder/Binder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-
-#include <input/Input.h>
-#include <input/InputTransport.h>
-
-#include <gtest/gtest.h>
-#include <inttypes.h>
-#include <linux/uinput.h>
-#include <log/log.h>
-#include <chrono>
-#include <thread>
-#include <unordered_map>
-
-#define TAG "InputFlingerServiceTest"
-
-using android::gui::FocusRequest;
-using android::os::BnInputFlinger;
-using android::os::IInputFlinger;
-
-using std::chrono_literals::operator""ms;
-using std::chrono_literals::operator""s;
-
-namespace android {
-
-static const String16 kTestServiceName = String16("InputFlingerService");
-static const String16 kQueryServiceName = String16("InputFlingerQueryService");
-
-// --- InputFlingerServiceTest ---
-class InputFlingerServiceTest : public testing::Test {
-public:
- void SetUp() override;
- void TearDown() override;
-
-protected:
- void InitializeInputFlinger();
-
- sp<IInputFlinger> mService;
- sp<IInputFlingerQuery> mQuery;
-
-private:
- std::unique_ptr<InputChannel> mServerChannel, mClientChannel;
- std::mutex mLock;
-};
-
-
-class TestInputManager : public BnInputFlinger {
-protected:
- virtual ~TestInputManager(){};
-
-public:
- TestInputManager(){};
-
- binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
-
- status_t dump(int fd, const Vector<String16>& args) override;
-
- binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
- binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
- binder::Status setFocusedWindow(const FocusRequest&) override;
-
- void reset();
-
-private:
- mutable Mutex mLock;
- std::vector<std::shared_ptr<InputChannel>> mInputChannels;
-};
-
-class TestInputQuery : public BnInputFlingerQuery {
-public:
- TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){};
- binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
- binder::Status resetInputManager() override;
-
-private:
- sp<android::TestInputManager> mManager;
-};
-
-binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) {
- return mManager->getInputChannels(channels);
-}
-
-binder::Status TestInputQuery::resetInputManager() {
- mManager->reset();
- return binder::Status::ok();
-}
-
-binder::Status TestInputManager::createInputChannel(const std::string& name,
- InputChannel* outChannel) {
- AutoMutex _l(mLock);
- std::unique_ptr<InputChannel> serverChannel;
- std::unique_ptr<InputChannel> clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-
- clientChannel->copyTo(*outChannel);
-
- mInputChannels.emplace_back(std::move(serverChannel));
-
- return binder::Status::ok();
-}
-
-binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
- AutoMutex _l(mLock);
-
- auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
- [&](std::shared_ptr<InputChannel>& c) {
- return c->getConnectionToken() == connectionToken;
- });
- if (it != mInputChannels.end()) {
- mInputChannels.erase(it);
- }
-
- return binder::Status::ok();
-}
-
-status_t TestInputManager::dump(int fd, const Vector<String16>& args) {
- std::string dump;
-
- dump += " InputFlinger dump\n";
-
- ::write(fd, dump.c_str(), dump.size());
- return NO_ERROR;
-}
-
-binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) {
- channels->clear();
- for (std::shared_ptr<InputChannel>& channel : mInputChannels) {
- channels->push_back(*channel);
- }
- return binder::Status::ok();
-}
-
-binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) {
- return binder::Status::ok();
-}
-
-void TestInputManager::reset() {
- mInputChannels.clear();
-}
-
-void InputFlingerServiceTest::SetUp() {
- InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
- InitializeInputFlinger();
-}
-
-void InputFlingerServiceTest::TearDown() {
- mQuery->resetInputManager();
-}
-
-void InputFlingerServiceTest::InitializeInputFlinger() {
- sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName));
- ASSERT_TRUE(input != nullptr);
- mService = interface_cast<IInputFlinger>(input);
-
- input = defaultServiceManager()->waitForService(kQueryServiceName);
- ASSERT_TRUE(input != nullptr);
- mQuery = interface_cast<IInputFlingerQuery>(input);
-}
-
-/**
- * Test InputFlinger service interface createInputChannel
- */
-TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
- // Test that the unblocked file descriptor flag is kept across processes over binder
- // transactions.
-
- InputChannel channel;
- ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
-
- const base::unique_fd& fd = channel.getFd();
- ASSERT_TRUE(fd.ok());
-
- const int result = fcntl(fd, F_GETFL);
- EXPECT_NE(result, -1);
- EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
-}
-
-TEST_F(InputFlingerServiceTest, CreateInputChannel) {
- InputChannel channel;
- ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
-
- std::vector<::android::InputChannel> channels;
- mQuery->getInputChannels(&channels);
- ASSERT_EQ(channels.size(), 1UL);
- EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
-
- mService->removeInputChannel(channel.getConnectionToken());
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 0UL);
-}
-
-} // namespace android
-
-int main(int argc, char** argv) {
- pid_t forkPid = fork();
-
- if (forkPid == 0) {
- // Server process
- android::sp<android::TestInputManager> manager =
- android::sp<android::TestInputManager>::make();
- android::sp<android::TestInputQuery> query =
- android::sp<android::TestInputQuery>::make(manager);
-
- android::defaultServiceManager()->addService(android::kTestServiceName, manager,
- false /*allowIsolated*/);
- android::defaultServiceManager()->addService(android::kQueryServiceName, query,
- false /*allowIsolated*/);
- android::ProcessState::self()->startThreadPool();
- android::IPCThreadState::self()->joinThreadPool();
- } else {
- android::ProcessState::self()->startThreadPool();
- ::testing::InitGoogleTest(&argc, argv);
- int result = RUN_ALL_TESTS();
- kill(forkPid, SIGKILL);
- return result;
- }
- return 0;
-}
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
new file mode 100644
index 0000000..3cd7c1b
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "InputMapperTest.h"
+
+#include <InputReaderBase.h>
+#include <gtest/gtest.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
+const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+ ftl::Flags<InputDeviceClass>(0); // not needed for current tests
+
+void InputMapperTest::SetUp(ftl::Flags<InputDeviceClass> classes, int bus) {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
+ // Consume the device reset notification generated when adding a new device.
+ mFakeListener->assertNotifyDeviceResetWasCalled();
+}
+
+void InputMapperTest::SetUp() {
+ SetUp(DEVICE_CLASSES);
+}
+
+void InputMapperTest::TearDown() {
+ mFakeListener.reset();
+ mFakePolicy.clear();
+}
+
+void InputMapperTest::addConfigurationProperty(const char* key, const char* value) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
+}
+
+std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
+ if (!changes ||
+ (changes &
+ (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ std::list<NotifyArgs> out =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& args : out) {
+ mFakeListener->notify(args);
+ }
+ mReader->loopOnce();
+ return out;
+}
+
+std::shared_ptr<InputDevice> InputMapperTest::newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location,
+ int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes,
+ int bus) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ identifier.bus = bus;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes, bus);
+ mReader->loopOnce();
+ return device;
+}
+
+void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
+ uniqueId, physicalPort, viewportType);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
+void InputMapperTest::clearViewports() {
+ mFakePolicy->clearViewports();
+}
+
+std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when, nsecs_t readTime,
+ int32_t type, int32_t code, int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = mapper.getDeviceContext().getEventHubId();
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::list<NotifyArgs> processArgList = mapper.process(&event);
+ for (const NotifyArgs& args : processArgList) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return processArgList;
+}
+
+void InputMapperTest::resetMapper(InputMapper& mapper, nsecs_t when) {
+ const auto resetArgs = mapper.reset(when);
+ for (const auto args : resetArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+}
+
+std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_t when) {
+ std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+}
+
+void InputMapperTest::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;
+ ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
+ 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) {
+ 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);
+ ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
+ ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
+ ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
+}
+
+void InputMapperTest::assertPosition(const FakePointerController& controller, float x, float y) {
+ float actualX, actualY;
+ controller.getPosition(&actualX, &actualY);
+ ASSERT_NEAR(x, actualX, 1);
+ ASSERT_NEAR(y, actualY, 1);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
new file mode 100644
index 0000000..b3401c3
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -0,0 +1,95 @@
+/*
+ * 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 <list>
+#include <memory>
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class InputMapperTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t DEVICE_GENERATION = 2;
+ static constexpr int32_t DEVICE_CONTROLLER_NUMBER = 0;
+ static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ void SetUp() override;
+ void TearDown() override;
+
+ void addConfigurationProperty(const char* key, const char* value);
+ std::list<NotifyArgs> configureDevice(uint32_t changes);
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ template <class T, typename... Args>
+ T& addMapperAndConfigure(Args... args) {
+ T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
+ configureDevice(0);
+ std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+ resetArgList += mapper.reset(ARBITRARY_TIME);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& loopArgs : resetArgList) {
+ mFakeListener->notify(loopArgs);
+ }
+ mReader->loopOnce();
+ return mapper;
+ }
+
+ void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType);
+ void clearViewports();
+ std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+ int32_t code, int32_t value);
+ 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);
+ static void assertPosition(const FakePointerController& controller, float x, float y);
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 78ea692..4cc48f6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -36,9 +36,18 @@
#include <UinputDevice.h>
#include <VibratorInputMapper.h>
#include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
+#include <ui/Rotation.h>
+#include <thread>
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "FakePointerController.h"
+#include "InputMapperTest.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
#include "android/hardware/input/InputDeviceCountryCode.h"
#include "input/DisplayViewport.h"
#include "input/Input.h"
@@ -51,13 +60,6 @@
using testing::AllOf;
using std::chrono_literals::operator""ms;
-// Timeout for waiting for an expected event
-static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
-
-// An arbitrary time value.
-static constexpr nsecs_t ARBITRARY_TIME = 1234;
-static constexpr nsecs_t READ_TIME = 4321;
-
// Arbitrary display properties.
static constexpr int32_t DISPLAY_ID = 0;
static const std::string DISPLAY_UNIQUE_ID = "local:1";
@@ -78,10 +80,6 @@
static constexpr int32_t FIRST_TRACKING_ID = 0;
static constexpr int32_t SECOND_TRACKING_ID = 1;
static constexpr int32_t THIRD_TRACKING_ID = 2;
-static constexpr int32_t DEFAULT_BATTERY = 1;
-static constexpr int32_t BATTERY_STATUS = 4;
-static constexpr int32_t BATTERY_CAPACITY = 66;
-static const std::string BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
static constexpr int32_t LIGHT_COLOR = 0x7F448866;
static constexpr int32_t LIGHT_PLAYER_ID = 2;
@@ -95,8 +93,10 @@
static constexpr int32_t ACTION_POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-// Error tolerance for floating point assertions.
-static const float EPSILON = 0.001f;
+// 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);
template<typename T>
static inline T min(T a, T b) {
@@ -112,12 +112,12 @@
{"green", LightColor::GREEN},
{"blue", LightColor::BLUE}};
-static int32_t getInverseRotation(int32_t orientation) {
+static ui::Rotation getInverseRotation(ui::Rotation orientation) {
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- return DISPLAY_ORIENTATION_270;
- case DISPLAY_ORIENTATION_270:
- return DISPLAY_ORIENTATION_90;
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
default:
return orientation;
}
@@ -141,940 +141,15 @@
ASSERT_EQ(nullptr, motionRange);
}
-// --- FakePointerController ---
-
-class FakePointerController : public PointerControllerInterface {
- bool mHaveBounds;
- float mMinX, mMinY, mMaxX, mMaxY;
- float mX, mY;
- int32_t mButtonState;
- int32_t mDisplayId;
-
-public:
- FakePointerController() :
- mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
- mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
+[[maybe_unused]] static void dumpReader(InputReader& reader) {
+ std::string dump;
+ reader.dump(dump);
+ std::istringstream iss(dump);
+ for (std::string line; std::getline(iss, line);) {
+ ALOGE("%s", line.c_str());
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
-
- virtual ~FakePointerController() {}
-
- void setBounds(float minX, float minY, float maxX, float maxY) {
- mHaveBounds = true;
- mMinX = minX;
- mMinY = minY;
- mMaxX = maxX;
- mMaxY = maxY;
- }
-
- void setPosition(float x, float y) override {
- mX = x;
- mY = y;
- }
-
- void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
-
- int32_t getButtonState() const override { return mButtonState; }
-
- void getPosition(float* outX, float* outY) const override {
- *outX = mX;
- *outY = mY;
- }
-
- int32_t getDisplayId() const override { return mDisplayId; }
-
- void setDisplayViewport(const DisplayViewport& viewport) override {
- mDisplayId = viewport.displayId;
- }
-
- const std::map<int32_t, std::vector<int32_t>>& getSpots() {
- return mSpotsByDisplay;
- }
-
-private:
- bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
- *outMinX = mMinX;
- *outMinY = mMinY;
- *outMaxX = mMaxX;
- *outMaxY = mMaxY;
- return mHaveBounds;
- }
-
- void move(float deltaX, float deltaY) override {
- mX += deltaX;
- if (mX < mMinX) mX = mMinX;
- if (mX > mMaxX) mX = mMaxX;
- mY += deltaY;
- if (mY < mMinY) mY = mMinY;
- if (mY > mMaxY) mY = mMaxY;
- }
-
- void fade(Transition) override {}
-
- void unfade(Transition) override {}
-
- void setPresentation(Presentation) override {}
-
- void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
- int32_t displayId) override {
- std::vector<int32_t> newSpots;
- // Add spots for fingers that are down.
- for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- newSpots.push_back(id);
- }
-
- mSpotsByDisplay[displayId] = newSpots;
- }
-
- void clearSpots() override { mSpotsByDisplay.clear(); }
-
- std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
-};
-
-
-// --- FakeInputReaderPolicy ---
-
-class FakeInputReaderPolicy : public InputReaderPolicyInterface {
- std::mutex mLock;
- std::condition_variable mDevicesChangedCondition;
-
- InputReaderConfiguration mConfig;
- std::shared_ptr<FakePointerController> mPointerController;
- std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
- bool mInputDevicesChanged GUARDED_BY(mLock){false};
- std::vector<DisplayViewport> mViewports;
- TouchAffineTransformation transform;
-
-protected:
- virtual ~FakeInputReaderPolicy() {}
-
-public:
- FakeInputReaderPolicy() {
- }
-
- void assertInputDevicesChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (!devicesChanged) {
- FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
- }
- });
- }
-
- void assertInputDevicesNotChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (devicesChanged) {
- FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
- }
- });
- }
-
- virtual void clearViewports() {
- mViewports.clear();
- mConfig.setDisplayViewports(mViewports);
- }
-
- std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
- return mConfig.getDisplayViewportByUniqueId(uniqueId);
- }
- std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
- return mConfig.getDisplayViewportByType(type);
- }
-
- std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
- return mConfig.getDisplayViewportByPort(displayPort);
- }
-
- void addDisplayViewport(DisplayViewport viewport) {
- mViewports.push_back(std::move(viewport));
- mConfig.setDisplayViewports(mViewports);
- }
-
- void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType type) {
- const bool isRotated =
- (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- v.isActive = isActive;
- v.uniqueId = uniqueId;
- v.physicalPort = physicalPort;
- v.type = type;
-
- addDisplayViewport(v);
- }
-
- bool updateViewport(const DisplayViewport& viewport) {
- size_t count = mViewports.size();
- for (size_t i = 0; i < count; i++) {
- const DisplayViewport& currentViewport = mViewports[i];
- if (currentViewport.displayId == viewport.displayId) {
- mViewports[i] = viewport;
- mConfig.setDisplayViewports(mViewports);
- return true;
- }
- }
- // no viewport found.
- return false;
- }
-
- void addExcludedDeviceName(const std::string& deviceName) {
- mConfig.excludedDeviceNames.push_back(deviceName);
- }
-
- void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
- mConfig.portAssociations.insert({inputPort, displayPort});
- }
-
- void addInputUniqueIdAssociation(const std::string& inputUniqueId,
- const std::string& displayUniqueId) {
- mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
- }
-
- void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
-
- void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
-
- void setPointerController(std::shared_ptr<FakePointerController> controller) {
- mPointerController = std::move(controller);
- }
-
- const InputReaderConfiguration* getReaderConfiguration() const {
- return &mConfig;
- }
-
- const std::vector<InputDeviceInfo>& getInputDevices() const {
- return mInputDevices;
- }
-
- TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) {
- return transform;
- }
-
- void setTouchAffineTransformation(const TouchAffineTransformation t) {
- transform = t;
- }
-
- PointerCaptureRequest setPointerCapture(bool enabled) {
- mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
- return mConfig.pointerCaptureRequest;
- }
-
- void setShowTouches(bool enabled) {
- mConfig.showTouches = enabled;
- }
-
- void setDefaultPointerDisplayId(int32_t pointerDisplayId) {
- mConfig.defaultPointerDisplayId = pointerDisplayId;
- }
-
- void setPointerGestureEnabled(bool enabled) { mConfig.pointerGesturesEnabled = enabled; }
-
- float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
-
- float getPointerGestureZoomSpeedRatio() { return mConfig.pointerGestureZoomSpeedRatio; }
-
- void setVelocityControlParams(const VelocityControlParameters& params) {
- mConfig.pointerVelocityControlParameters = params;
- mConfig.wheelVelocityControlParameters = params;
- }
-
-private:
- uint32_t mNextPointerCaptureSequenceNumber = 0;
-
- void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
- *outConfig = mConfig;
- }
-
- std::shared_ptr<PointerControllerInterface> obtainPointerController(
- int32_t /*deviceId*/) override {
- return mPointerController;
- }
-
- void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
- std::scoped_lock<std::mutex> lock(mLock);
- mInputDevices = inputDevices;
- mInputDevicesChanged = true;
- mDevicesChangedCondition.notify_all();
- }
-
- std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) override {
- return nullptr;
- }
-
- std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
-
- void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
- std::unique_lock<std::mutex> lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- const bool devicesChanged =
- mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
- return mInputDevicesChanged;
- });
- ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
- mInputDevicesChanged = false;
- }
-};
-
-// --- FakeEventHub ---
-
-class FakeEventHub : public EventHubInterface {
- struct KeyInfo {
- int32_t keyCode;
- uint32_t flags;
- };
-
- struct SensorInfo {
- InputDeviceSensorType sensorType;
- int32_t sensorDataIndex;
- };
-
- struct Device {
- InputDeviceIdentifier identifier;
- ftl::Flags<InputDeviceClass> classes;
- PropertyMap configuration;
- KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
- KeyedVector<int, bool> relativeAxes;
- KeyedVector<int32_t, int32_t> keyCodeStates;
- KeyedVector<int32_t, int32_t> scanCodeStates;
- KeyedVector<int32_t, int32_t> switchStates;
- KeyedVector<int32_t, int32_t> absoluteAxisValue;
- KeyedVector<int32_t, KeyInfo> keysByScanCode;
- KeyedVector<int32_t, KeyInfo> keysByUsageCode;
- KeyedVector<int32_t, bool> leds;
- // fake mapping which would normally come from keyCharacterMap
- std::unordered_map<int32_t, int32_t> keyCodeMapping;
- std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
- BitArray<MSC_MAX> mscBitmask;
- std::vector<VirtualKeyDefinition> virtualKeys;
- bool enabled;
- InputDeviceCountryCode countryCode;
-
- status_t enable() {
- enabled = true;
- return OK;
- }
-
- status_t disable() {
- enabled = false;
- return OK;
- }
-
- explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
- };
-
- std::mutex mLock;
- std::condition_variable mEventsCondition;
-
- KeyedVector<int32_t, Device*> mDevices;
- std::vector<std::string> mExcludedDevices;
- std::vector<RawEvent> mEvents GUARDED_BY(mLock);
- std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
- std::vector<int32_t> mVibrators = {0, 1};
- std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
- // Simulates a device light brightness, from light id to light brightness.
- std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
- // Simulates a device light intensities, from light id to light intensities map.
- std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
- mLightIntensities;
-
-public:
- virtual ~FakeEventHub() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- delete mDevices.valueAt(i);
- }
- }
-
- FakeEventHub() { }
-
- void addDevice(int32_t deviceId, const std::string& name,
- ftl::Flags<InputDeviceClass> classes) {
- Device* device = new Device(classes);
- device->identifier.name = name;
- mDevices.add(deviceId, device);
-
- enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
- }
-
- void removeDevice(int32_t deviceId) {
- delete mDevices.valueFor(deviceId);
- mDevices.removeItem(deviceId);
-
- enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
- }
-
- bool isDeviceEnabled(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- if (device == nullptr) {
- ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
- return false;
- }
- return device->enabled;
- }
-
- status_t enableDevice(int32_t deviceId) override {
- status_t result;
- Device* device = getDevice(deviceId);
- if (device == nullptr) {
- ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
- return BAD_VALUE;
- }
- if (device->enabled) {
- ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
- return OK;
- }
- result = device->enable();
- return result;
- }
-
- status_t disableDevice(int32_t deviceId) override {
- Device* device = getDevice(deviceId);
- if (device == nullptr) {
- ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
- return BAD_VALUE;
- }
- if (!device->enabled) {
- ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
- return OK;
- }
- return device->disable();
- }
-
- void finishDeviceScan() {
- enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
- }
-
- void addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
- Device* device = getDevice(deviceId);
- device->configuration.addProperty(key, value);
- }
-
- void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
- Device* device = getDevice(deviceId);
- device->configuration.addAll(configuration);
- }
-
- void addAbsoluteAxis(int32_t deviceId, int axis,
- int32_t minValue, int32_t maxValue, int flat, int fuzz, int resolution = 0) {
- Device* device = getDevice(deviceId);
-
- RawAbsoluteAxisInfo info;
- info.valid = true;
- info.minValue = minValue;
- info.maxValue = maxValue;
- info.flat = flat;
- info.fuzz = fuzz;
- info.resolution = resolution;
- device->absoluteAxes.add(axis, info);
- }
-
- void addRelativeAxis(int32_t deviceId, int32_t axis) {
- Device* device = getDevice(deviceId);
- device->relativeAxes.add(axis, true);
- }
-
- void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->keyCodeStates.replaceValueFor(keyCode, state);
- }
-
- void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
- Device* device = getDevice(deviceId);
- device->countryCode = countryCode;
- }
-
- void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->scanCodeStates.replaceValueFor(scanCode, state);
- }
-
- void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->switchStates.replaceValueFor(switchCode, state);
- }
-
- void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
- Device* device = getDevice(deviceId);
- device->absoluteAxisValue.replaceValueFor(axis, value);
- }
-
- void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t keyCode, uint32_t flags) {
- Device* device = getDevice(deviceId);
- KeyInfo info;
- info.keyCode = keyCode;
- info.flags = flags;
- if (scanCode) {
- device->keysByScanCode.add(scanCode, info);
- }
- if (usageCode) {
- device->keysByUsageCode.add(usageCode, info);
- }
- }
-
- void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
- Device* device = getDevice(deviceId);
- device->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
- }
-
- void addLed(int32_t deviceId, int32_t led, bool initialState) {
- Device* device = getDevice(deviceId);
- device->leds.add(led, initialState);
- }
-
- void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
- int32_t sensorDataIndex) {
- Device* device = getDevice(deviceId);
- SensorInfo info;
- info.sensorType = sensorType;
- info.sensorDataIndex = sensorDataIndex;
- device->sensorsByAbsCode.emplace(absCode, info);
- }
-
- void setMscEvent(int32_t deviceId, int32_t mscEvent) {
- Device* device = getDevice(deviceId);
- typename BitArray<MSC_MAX>::Buffer buffer;
- buffer[mscEvent / 32] = 1 << mscEvent % 32;
- device->mscBitmask.loadFromBuffer(buffer);
- }
-
- void addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
- mRawLightInfos.emplace(rawId, std::move(info));
- }
-
- void fakeLightBrightness(int32_t rawId, int32_t brightness) {
- mLightBrightness.emplace(rawId, brightness);
- }
-
- void fakeLightIntensities(int32_t rawId,
- const std::unordered_map<LightColor, int32_t> intensities) {
- mLightIntensities.emplace(rawId, std::move(intensities));
- }
-
- bool getLedState(int32_t deviceId, int32_t led) {
- Device* device = getDevice(deviceId);
- return device->leds.valueFor(led);
- }
-
- std::vector<std::string>& getExcludedDevices() {
- return mExcludedDevices;
- }
-
- void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
- Device* device = getDevice(deviceId);
- device->virtualKeys.push_back(definition);
- }
-
- void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
- int32_t value) {
- std::scoped_lock<std::mutex> lock(mLock);
- RawEvent event;
- event.when = when;
- event.readTime = readTime;
- event.deviceId = deviceId;
- event.type = type;
- event.code = code;
- event.value = value;
- mEvents.push_back(event);
-
- if (type == EV_ABS) {
- setAbsoluteAxisValue(deviceId, code, value);
- }
- }
-
- void setVideoFrames(std::unordered_map<int32_t /*deviceId*/,
- std::vector<TouchVideoFrame>> videoFrames) {
- mVideoFrames = std::move(videoFrames);
- }
-
- void assertQueueIsEmpty() {
- std::unique_lock<std::mutex> lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
- const bool queueIsEmpty =
- mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
- [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
- if (!queueIsEmpty) {
- FAIL() << "Timed out waiting for EventHub queue to be emptied.";
- }
- }
-
-private:
- Device* getDevice(int32_t deviceId) const {
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : nullptr;
- }
-
- ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- return device ? device->classes : ftl::Flags<InputDeviceClass>(0);
- }
-
- InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- return device ? device->identifier : InputDeviceIdentifier();
- }
-
- int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
-
- void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- *outConfiguration = device->configuration;
- }
- }
-
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override {
- Device* device = getDevice(deviceId);
- if (device && device->enabled) {
- ssize_t index = device->absoluteAxes.indexOfKey(axis);
- if (index >= 0) {
- *outAxisInfo = device->absoluteAxes.valueAt(index);
- return OK;
- }
- }
- outAxisInfo->clear();
- return -1;
- }
-
- bool hasRelativeAxis(int32_t deviceId, int axis) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- return device->relativeAxes.indexOfKey(axis) >= 0;
- }
- return false;
- }
-
- bool hasInputProperty(int32_t, int) const override { return false; }
-
- bool hasMscEvent(int32_t deviceId, int mscEvent) const override final {
- Device* device = getDevice(deviceId);
- if (device) {
- return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
- }
- return false;
- }
-
- status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- const KeyInfo* key = getKey(device, scanCode, usageCode);
- if (key) {
- if (outKeycode) {
- *outKeycode = key->keyCode;
- }
- if (outFlags) {
- *outFlags = key->flags;
- }
- if (outMetaState) {
- *outMetaState = metaState;
- }
- return OK;
- }
- }
- return NAME_NOT_FOUND;
- }
-
- const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const {
- if (usageCode) {
- ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- return &device->keysByUsageCode.valueAt(index);
- }
- }
- if (scanCode) {
- ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- return &device->keysByScanCode.valueAt(index);
- }
- }
- return nullptr;
- }
-
- status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
-
- base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
- int32_t deviceId, int32_t absCode) const override {
- Device* device = getDevice(deviceId);
- if (!device) {
- return Errorf("Sensor device not found.");
- }
- auto it = device->sensorsByAbsCode.find(absCode);
- if (it == device->sensorsByAbsCode.end()) {
- return Errorf("Sensor map not found.");
- }
- const SensorInfo& info = it->second;
- return std::make_pair(info.sensorType, info.sensorDataIndex);
- }
-
- void setExcludedDevices(const std::vector<std::string>& devices) override {
- mExcludedDevices = devices;
- }
-
- std::vector<RawEvent> getEvents(int) override {
- std::scoped_lock lock(mLock);
-
- std::vector<RawEvent> buffer;
- std::swap(buffer, mEvents);
-
- mEventsCondition.notify_all();
- return buffer;
- }
-
- std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
- auto it = mVideoFrames.find(deviceId);
- if (it != mVideoFrames.end()) {
- std::vector<TouchVideoFrame> frames = std::move(it->second);
- mVideoFrames.erase(deviceId);
- return frames;
- }
- return {};
- }
-
- int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
- if (index >= 0) {
- return device->scanCodeStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- return device->countryCode;
- }
- return InputDeviceCountryCode::INVALID;
- }
-
- int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
- if (index >= 0) {
- return device->keyCodeStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->switchStates.indexOfKey(sw);
- if (index >= 0) {
- return device->switchStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
- if (index >= 0) {
- *outValue = device->absoluteAxisValue.valueAt(index);
- return OK;
- }
- }
- *outValue = 0;
- return -1;
- }
-
- int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
- Device* device = getDevice(deviceId);
- if (!device) {
- return AKEYCODE_UNKNOWN;
- }
- auto it = device->keyCodeMapping.find(locationKeyCode);
- return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
- }
-
- // Return true if the device has non-empty key layout.
- bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
- uint8_t* outFlags) const override {
- bool result = false;
- Device* device = getDevice(deviceId);
- if (device) {
- result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
- for (size_t i = 0; i < keyCodes.size(); i++) {
- for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
- if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
- outFlags[i] = 1;
- }
- }
- for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
- if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
- outFlags[i] = 1;
- }
- }
- }
- }
- return result;
- }
-
- bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
- return index >= 0;
- }
- return false;
- }
-
- bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
- Device* device = getDevice(deviceId);
- if (!device) {
- return false;
- }
- for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
- if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
- return true;
- }
- }
- for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
- if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
- return true;
- }
- }
- return false;
- }
-
- bool hasLed(int32_t deviceId, int32_t led) const override {
- Device* device = getDevice(deviceId);
- return device && device->leds.indexOfKey(led) >= 0;
- }
-
- void setLedState(int32_t deviceId, int32_t led, bool on) override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->leds.indexOfKey(led);
- if (index >= 0) {
- device->leds.replaceValueAt(led, on);
- } else {
- ADD_FAILURE()
- << "Attempted to set the state of an LED that the EventHub declared "
- "was not present. led=" << led;
- }
- }
- }
-
- void getVirtualKeyDefinitions(
- int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
- outVirtualKeys.clear();
-
- Device* device = getDevice(deviceId);
- if (device) {
- outVirtualKeys = device->virtualKeys;
- }
- }
-
- const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
- return nullptr;
- }
-
- bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
- return false;
- }
-
- void vibrate(int32_t, const VibrationElement&) override {}
-
- void cancelVibrate(int32_t) override {}
-
- std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return mVibrators; };
-
- std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
- return BATTERY_CAPACITY;
- }
-
- std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
- return BATTERY_STATUS;
- }
-
- std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override {
- return {DEFAULT_BATTERY};
- }
-
- std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
- int32_t batteryId) const override {
- if (batteryId != DEFAULT_BATTERY) return {};
- static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
- .name = "default battery",
- .flags = InputBatteryClass::CAPACITY,
- .path = BATTERY_DEVPATH};
- return BATTERY_INFO;
- }
-
- std::vector<int32_t> getRawLightIds(int32_t deviceId) const override {
- std::vector<int32_t> ids;
- for (const auto& [rawId, info] : mRawLightInfos) {
- ids.push_back(rawId);
- }
- return ids;
- }
-
- std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override {
- auto it = mRawLightInfos.find(lightId);
- if (it == mRawLightInfos.end()) {
- return std::nullopt;
- }
- return it->second;
- }
-
- void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override {
- mLightBrightness.emplace(lightId, brightness);
- }
-
- void setLightIntensities(int32_t deviceId, int32_t lightId,
- std::unordered_map<LightColor, int32_t> intensities) override {
- mLightIntensities.emplace(lightId, intensities);
- };
-
- std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override {
- auto lightIt = mLightBrightness.find(lightId);
- if (lightIt == mLightBrightness.end()) {
- return std::nullopt;
- }
- return lightIt->second;
- }
-
- std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
- int32_t deviceId, int32_t lightId) const override {
- auto lightIt = mLightIntensities.find(lightId);
- if (lightIt == mLightIntensities.end()) {
- return std::nullopt;
- }
- return lightIt->second;
- };
-
- void dump(std::string&) const override {}
-
- void monitor() const override {}
-
- void requestReopenDevices() override {}
-
- void wake() override {}
-};
+}
// --- FakeInputMapper ---
@@ -1190,7 +265,8 @@
}
}
- void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
+ std::list<NotifyArgs> configure(nsecs_t, const InputReaderConfiguration* config,
+ uint32_t changes) override {
std::scoped_lock<std::mutex> lock(mLock);
mConfigureWasCalled = true;
@@ -1201,19 +277,22 @@
}
mStateChangedCondition.notify_all();
+ return {};
}
- void reset(nsecs_t) override {
+ std::list<NotifyArgs> reset(nsecs_t) override {
std::scoped_lock<std::mutex> lock(mLock);
mResetWasCalled = true;
mStateChangedCondition.notify_all();
+ return {};
}
- void process(const RawEvent* rawEvent) override {
+ std::list<NotifyArgs> process(const RawEvent* rawEvent) override {
std::scoped_lock<std::mutex> lock(mLock);
mLastEvent = *rawEvent;
mProcessWasCalled = true;
mStateChangedCondition.notify_all();
+ return {};
}
int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
@@ -1265,92 +344,6 @@
}
};
-
-// --- InstrumentedInputReader ---
-
-class InstrumentedInputReader : public InputReader {
- std::queue<std::shared_ptr<InputDevice>> mNextDevices;
-
-public:
- InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- InputListenerInterface& listener)
- : InputReader(eventHub, policy, listener), mFakeContext(this) {}
-
- virtual ~InstrumentedInputReader() {}
-
- void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location = "") {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- int32_t generation = deviceId + 1;
- return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
- }
-
- // Make the protected loopOnce method accessible to tests.
- using InputReader::loopOnce;
-
-protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId,
- const InputDeviceIdentifier& identifier)
- REQUIRES(mLock) {
- if (!mNextDevices.empty()) {
- std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
- mNextDevices.pop();
- return device;
- }
- return InputReader::createDeviceLocked(eventHubId, identifier);
- }
-
- // --- FakeInputReaderContext ---
- class FakeInputReaderContext : public ContextImpl {
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
-
- public:
- FakeInputReaderContext(InputReader* reader)
- : ContextImpl(reader),
- mGlobalMetaState(0),
- mUpdateGlobalMetaStateWasCalled(false),
- mGeneration(1) {}
-
- virtual ~FakeInputReaderContext() {}
-
- void assertUpdateGlobalMetaStateWasCalled() {
- ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
- << "Expected updateGlobalMetaState() to have been called.";
- mUpdateGlobalMetaStateWasCalled = false;
- }
-
- void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
-
- uint32_t getGeneration() { return mGeneration; }
-
- void updateGlobalMetaState() override {
- mUpdateGlobalMetaStateWasCalled = true;
- ContextImpl::updateGlobalMetaState();
- }
-
- int32_t getGlobalMetaState() override {
- return mGlobalMetaState | ContextImpl::getGlobalMetaState();
- }
-
- int32_t bumpGeneration() override {
- mGeneration = ContextImpl::bumpGeneration();
- return mGeneration;
- }
- } mFakeContext;
-
- friend class InputReaderTest;
-
-public:
- FakeInputReaderContext* getContext() { return &mFakeContext; }
-};
-
// --- InputReaderPolicyTest ---
class InputReaderPolicyTest : public testing::Test {
protected:
@@ -1376,9 +369,8 @@
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId, NO_PORT, ViewportType::INTERNAL);
// Check matching by uniqueId
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
@@ -1407,21 +399,21 @@
constexpr int32_t virtualDisplayId2 = 3;
// Add an internal viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, internalUniqueId,
- NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, internalUniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Add an external viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, externalUniqueId,
- NO_PORT, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, externalUniqueId, NO_PORT,
+ ViewportType::EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId1,
- NO_PORT, ViewportType::VIRTUAL);
+ ui::ROTATION_0, true /*isActive*/, virtualUniqueId1, NO_PORT,
+ ViewportType::VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId2,
- NO_PORT, ViewportType::VIRTUAL);
+ ui::ROTATION_0, true /*isActive*/, virtualUniqueId2, NO_PORT,
+ ViewportType::VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
@@ -1469,13 +461,11 @@
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1,
- NO_PORT, type);
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId1, NO_PORT, type);
// Add another viewport
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2,
- NO_PORT, type);
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId2, NO_PORT, type);
// Check that correct display viewport was returned by comparing the display IDs.
std::optional<DisplayViewport> viewport1 =
@@ -1515,10 +505,10 @@
// Add the default display first and ensure it gets returned.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
std::optional<DisplayViewport> viewport =
@@ -1530,10 +520,10 @@
// Add the default display second to make sure order doesn't matter.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
@@ -1557,13 +547,11 @@
mFakePolicy->clearViewports();
// Add a viewport that's associated with some display port that's not of interest.
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, hdmi3,
- type);
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId1, hdmi3, type);
// Add another viewport, connected to HDMI1 port
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, hdmi1,
- type);
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId2, hdmi1, type);
// Check that correct display viewport was returned by comparing the display ports.
std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
@@ -2016,11 +1004,10 @@
// Add default and second display.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, "local:0", NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, "local:0", NO_PORT, ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, "local:1", hdmi1,
+ ui::ROTATION_0, true /*isActive*/, "local:1", hdmi1,
ViewportType::EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
@@ -2219,8 +1206,9 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
- ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+ ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY),
+ FakeEventHub::BATTERY_CAPACITY);
+ ASSERT_EQ(mReader->getBatteryCapacity(deviceId), FakeEventHub::BATTERY_CAPACITY);
}
TEST_F(InputReaderTest, BatteryGetStatus) {
@@ -2236,8 +1224,9 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
- ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+ ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY),
+ FakeEventHub::BATTERY_STATUS);
+ ASSERT_EQ(mReader->getBatteryStatus(deviceId), FakeEventHub::BATTERY_STATUS);
}
TEST_F(InputReaderTest, BatteryGetDevicePath) {
@@ -2252,7 +1241,7 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), BATTERY_DEVPATH);
+ ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), FakeEventHub::BATTERY_DEVPATH);
}
TEST_F(InputReaderTest, LightGetColor) {
@@ -2296,6 +1285,9 @@
std::shared_ptr<FakePointerController> mFakePointerController;
void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
mFakePolicy = sp<FakeInputReaderPolicy>::make();
mFakePointerController = std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mFakePointerController);
@@ -2314,18 +1306,30 @@
}
void TearDown() override {
+#if !defined(__ANDROID__)
+ return;
+#endif
ASSERT_EQ(mReader->stop(), OK);
mReader.reset();
mTestListener.reset();
mFakePolicy.clear();
}
+
+ std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) {
+ const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
+ const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
+ [&name](const InputDeviceInfo& info) {
+ return info.getIdentifier().name == name;
+ });
+ return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
+ }
};
TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
// An invalid input device that is only used for this test.
class InvalidUinputDevice : public UinputDevice {
public:
- InvalidUinputDevice() : UinputDevice("Invalid Device") {}
+ InvalidUinputDevice() : UinputDevice("Invalid Device", 99 /*productId*/) {}
private:
void configureDevice(int fd, uinput_user_dev* device) override {}
@@ -2354,18 +1358,11 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
- // Find the test device by its name.
- const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
- const auto& it =
- std::find_if(inputDevices.begin(), inputDevices.end(),
- [&keyboard](const InputDeviceInfo& info) {
- return info.getIdentifier().name == keyboard->getName();
- });
-
- ASSERT_NE(it, inputDevices.end());
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources());
- ASSERT_EQ(0U, it->getMotionRanges().size());
+ const auto device = findDeviceByName(keyboard->getName());
+ ASSERT_TRUE(device.has_value());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources());
+ ASSERT_EQ(0U, device->getMotionRanges().size());
keyboard.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2400,6 +1397,41 @@
ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
}
+TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) {
+ std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+ const auto device = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(device.has_value());
+
+ // An external stylus with buttons should also be recognized as a keyboard.
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_STYLUS, device->getSources())
+ << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str();
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
+
+ const auto DOWN =
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD));
+ const auto UP = AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD));
+
+ stylus->pressAndReleaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
+
+ stylus->pressAndReleaseKey(BTN_STYLUS2);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
+
+ stylus->pressAndReleaseKey(BTN_STYLUS3);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
+ AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
+}
+
/**
* The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
* on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
@@ -2422,25 +1454,31 @@
ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode);
}
-// --- TouchProcessTest ---
+// --- TouchIntegrationTest ---
+
class TouchIntegrationTest : public InputReaderIntegrationTest {
protected:
const std::string UNIQUE_ID = "local:0";
void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
InputReaderIntegrationTest::SetUp();
// At least add an internal display.
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto info = findDeviceByName(mDevice->getName());
+ ASSERT_TRUE(info);
+ mDeviceInfo = *info;
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId,
+ ui::Rotation orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
@@ -2460,8 +1498,17 @@
}
std::unique_ptr<UinputTouchScreen> mDevice;
+ InputDeviceInfo mDeviceInfo;
};
+TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
+ // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus
+ // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button
+ // presses).
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD,
+ mDeviceInfo.getSources());
+}
+
TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -2676,6 +1723,466 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
+TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Send down with the pen tool selected. The policy should be notified of the stylus presence.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_PEN);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+
+ // Release the stylus touch.
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+ // Touch down with the finger, without the pen tool selected. The policy is not notified.
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+ // Send a move event with the stylus tool without BTN_TOUCH to generate a hover enter.
+ // The policy should be notified of the stylus presence.
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_PEN);
+ mDevice->sendMove(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+}
+
+// --- StylusButtonIntegrationTest ---
+
+// Verify the behavior of button presses reported by various kinds of styluses, including buttons
+// reported by the touchscreen's device, by a fused external stylus, and by an un-fused external
+// stylus.
+template <typename UinputStylusDevice>
+class StylusButtonIntegrationTest : public TouchIntegrationTest {
+protected:
+ void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
+ TouchIntegrationTest::SetUp();
+ mTouchscreen = mDevice.get();
+ mTouchscreenInfo = mDeviceInfo;
+
+ setUpStylusDevice();
+ }
+
+ UinputStylusDevice* mStylus{nullptr};
+ InputDeviceInfo mStylusInfo{};
+
+ UinputTouchScreen* mTouchscreen{nullptr};
+ InputDeviceInfo mTouchscreenInfo{};
+
+private:
+ // When we are attempting to test stylus button events that are sent from the touchscreen,
+ // use the same Uinput device for the touchscreen and the stylus.
+ template <typename T = UinputStylusDevice>
+ std::enable_if_t<std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() {
+ mStylus = mDevice.get();
+ mStylusInfo = mDeviceInfo;
+ }
+
+ // When we are attempting to stylus buttons from an external stylus being merged with touches
+ // from a touchscreen, create a new Uinput device through which stylus buttons can be injected.
+ template <typename T = UinputStylusDevice>
+ std::enable_if_t<!std::is_same_v<UinputTouchScreen, T>, void> setUpStylusDevice() {
+ mStylusDeviceLifecycleTracker = createUinputDevice<T>();
+ mStylus = mStylusDeviceLifecycleTracker.get();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto info = findDeviceByName(mStylus->getName());
+ ASSERT_TRUE(info);
+ mStylusInfo = *info;
+ }
+
+ std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{};
+
+ // Hide the base class's device to expose it with a different name for readability.
+ using TouchIntegrationTest::mDevice;
+ using TouchIntegrationTest::mDeviceInfo;
+};
+
+using StylusButtonIntegrationTestTypes =
+ ::testing::Types<UinputTouchScreen, UinputExternalStylus, UinputExternalStylusWithPressure>;
+TYPED_TEST_SUITE(StylusButtonIntegrationTest, StylusButtonIntegrationTestTypes);
+
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsGenerateKeyEvents) {
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+}
+
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingTouchGesture) {
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+
+ // Press the stylus button.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+
+ // Start and finish a stylus gesture.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Release the stylus button.
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+}
+
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsSurroundingHoveringTouchGesture) {
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+ auto toolTypeDevice =
+ AllOf(WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithDeviceId(touchscreenId));
+
+ // Press the stylus button.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+
+ // Start hovering with the stylus.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendMove(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Touch down with the stylus.
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Stop touching with the stylus, and start hovering.
+ TestFixture::mTouchscreen->sendUp();
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendMove(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Stop hovering.
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0))));
+ // TODO(b/257971675): Fix inconsistent button state when exiting hover.
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeDevice, WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Release the stylus button.
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+}
+
+TYPED_TEST(StylusButtonIntegrationTest, StylusButtonsWithinTouchGesture) {
+ const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
+ const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
+ const auto stylusId = TestFixture::mStylusInfo.getId();
+
+ // Start a stylus gesture.
+ TestFixture::mTouchscreen->sendSlot(FIRST_SLOT);
+ TestFixture::mTouchscreen->sendTrackingId(FIRST_TRACKING_ID);
+ TestFixture::mTouchscreen->sendToolType(MT_TOOL_PEN);
+ TestFixture::mTouchscreen->sendDown(centerPoint);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Press and release a stylus button. Each change in button state also generates a MOVE event.
+ TestFixture::mStylus->pressKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY),
+ WithDeviceId(touchscreenId))));
+
+ TestFixture::mStylus->releaseKey(BTN_STYLUS);
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyKeyWasCalled(
+ AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
+ WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY), WithDeviceId(stylusId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+
+ // Finish the stylus gesture.
+ TestFixture::mTouchscreen->sendTrackingId(INVALID_TRACKING_ID);
+ TestFixture::mTouchscreen->sendSync();
+ ASSERT_NO_FATAL_FAILURE(TestFixture::mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId))));
+}
+
+// --- ExternalStylusIntegrationTest ---
+
+// Verify the behavior of an external stylus. An external stylus can report pressure or button
+// data independently of the touchscreen, which is then sent as a MotionEvent as part of an
+// ongoing stylus gesture that is being emitted by the touchscreen.
+using ExternalStylusIntegrationTest = TouchIntegrationTest;
+
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Create an external stylus capable of reporting pressure data that
+ // should be fused with a touch pointer.
+ std::unique_ptr<UinputExternalStylusWithPressure> stylus =
+ createUinputDevice<UinputExternalStylusWithPressure>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+ const auto touchscreenId = mDeviceInfo.getId();
+
+ // Set a pressure value on the stylus. It doesn't generate any events.
+ const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX;
+ stylus->setPressure(100);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Start a finger gesture, and ensure it shows up as stylus gesture
+ // with the pressure set by the external stylus.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId), WithPressure(100.f / RAW_PRESSURE_MAX))));
+
+ // Change the pressure on the external stylus, and ensure the touchscreen generates a MOVE
+ // event with the updated pressure.
+ stylus->setPressure(200);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX))));
+
+ // The external stylus did not generate any events.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureNotReported) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Create an external stylus capable of reporting pressure data that
+ // should be fused with a touch pointer.
+ std::unique_ptr<UinputExternalStylusWithPressure> stylus =
+ createUinputDevice<UinputExternalStylusWithPressure>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+ const auto touchscreenId = mDeviceInfo.getId();
+
+ // Set a pressure value of 0 on the stylus. It doesn't generate any events.
+ const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX;
+ // Send a non-zero value first to prevent the kernel from consuming the zero event.
+ stylus->setPressure(100);
+ stylus->setPressure(0);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Start a finger gesture. The touch device will withhold generating any touches for
+ // up to 72 milliseconds while waiting for pressure data from the external stylus.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ auto waitUntil = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntil));
+
+ // Since the external stylus did not report a pressure value within the timeout,
+ // it shows up as a finger pointer.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDeviceId(touchscreenId),
+ WithPressure(1.f))));
+
+ // Change the pressure on the external stylus. Since the pressure was not present at the start
+ // of the gesture, it is ignored for now.
+ stylus->setPressure(200);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+
+ // Finish the finger gesture.
+ mDevice->sendTrackingId(INVALID_TRACKING_ID);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ // Start a new gesture. Since we have a valid pressure value, it shows up as a stylus.
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS), WithButtonState(0),
+ WithDeviceId(touchscreenId), WithPressure(200.f / RAW_PRESSURE_MAX))));
+
+ // The external stylus did not generate any events.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Create an external stylus device that does not support pressure. It should not affect any
+ // touch pointers.
+ std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto stylusInfo = findDeviceByName(stylus->getName());
+ ASSERT_TRUE(stylusInfo);
+
+ ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+ const auto touchscreenId = mDeviceInfo.getId();
+
+ // Start a finger gesture and ensure a finger pointer is generated for it, without waiting for
+ // pressure data from the external stylus.
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendToolType(MT_TOOL_FINGER);
+ mDevice->sendDown(centerPoint);
+ auto waitUntil = std::chrono::system_clock::now() +
+ std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener
+ ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(
+ AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithButtonState(0),
+ WithDeviceId(touchscreenId),
+ WithPressure(1.f)),
+ waitUntil));
+
+ // The external stylus did not generate any events.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
+}
+
// --- InputDeviceTest ---
class InputDeviceTest : public testing::Test {
protected:
@@ -2686,6 +2193,7 @@
static const int32_t DEVICE_CONTROLLER_NUMBER;
static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
+ static const std::string DEVICE_BLUETOOTH_ADDRESS;
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2702,6 +2210,7 @@
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
identifier.location = DEVICE_LOCATION;
+ identifier.bluetoothAddress = DEVICE_BLUETOOTH_ADDRESS;
mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
identifier);
mReader->pushNextDevice(mDevice);
@@ -2723,6 +2232,7 @@
const ftl::Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
const int32_t InputDeviceTest::EVENTHUB_ID = 1;
+const std::string InputDeviceTest::DEVICE_BLUETOOTH_ADDRESS = "11:AA:22:BB:33:CC";
TEST_F(InputDeviceTest, ImmutableProperties) {
ASSERT_EQ(DEVICE_ID, mDevice->getId());
@@ -2736,7 +2246,7 @@
// Configuration
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
InputReaderConfiguration config;
- mDevice->configure(ARBITRARY_TIME, &config, 0);
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode());
}
@@ -2748,10 +2258,10 @@
TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
// Configuration.
InputReaderConfiguration config;
- mDevice->configure(ARBITRARY_TIME, &config, 0);
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
// Reset.
- mDevice->reset(ARBITRARY_TIME);
+ unused += mDevice->reset(ARBITRARY_TIME);
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -2807,7 +2317,7 @@
mapper2.setMetaState(AMETA_SHIFT_ON);
InputReaderConfiguration config;
- mDevice->configure(ARBITRARY_TIME, &config, 0);
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, &config, 0);
std::string propertyValue;
ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty("key", propertyValue))
@@ -2818,7 +2328,7 @@
ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
// Reset
- mDevice->reset(ARBITRARY_TIME);
+ unused += mDevice->reset(ARBITRARY_TIME);
ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled());
@@ -2874,7 +2384,7 @@
// Event handling.
RawEvent event;
event.deviceId = EVENTHUB_ID;
- mDevice->process(&event, 1);
+ unused += mDevice->process(&event, 1);
ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
@@ -2887,7 +2397,8 @@
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
// First Configuration.
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
// Device should be enabled by default.
ASSERT_TRUE(mDevice->isEnabled());
@@ -2897,29 +2408,29 @@
const std::string UNIQUE_ID = "local:1";
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
// Device should be disabled because it is associated with a specific display via
// input port <-> display port association, but the corresponding display is not found
ASSERT_FALSE(mDevice->isEnabled());
// Prepare displays.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
+ ui::ROTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
ViewportType::INTERNAL);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
// Device should be disabled after set disable.
mFakePolicy->addDisabledDevice(mDevice->getId());
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_ENABLED_STATE);
ASSERT_FALSE(mDevice->isEnabled());
// Device should still be disabled even found the associated display.
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_FALSE(mDevice->isEnabled());
}
@@ -2927,47 +2438,49 @@
// Device should be enabled by default.
mFakePolicy->clearViewports();
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
ASSERT_TRUE(mDevice->isEnabled());
// Device should be disabled because it is associated with a specific display, but the
// corresponding display is not found.
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_FALSE(mDevice->isEnabled());
// Device should be enabled when a display is found.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
// Device should be disabled after set disable.
mFakePolicy->addDisabledDevice(mDevice->getId());
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_ENABLED_STATE);
ASSERT_FALSE(mDevice->isEnabled());
// Device should still be disabled even found the associated display.
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_FALSE(mDevice->isEnabled());
}
TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
mFakePolicy->clearViewports();
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
}
@@ -2986,161 +2499,11 @@
device.dump(dumpStr, eventHubDevStr);
}
-// --- InputMapperTest ---
-
-class InputMapperTest : public testing::Test {
-protected:
- static const char* DEVICE_NAME;
- static const char* DEVICE_LOCATION;
- static const int32_t DEVICE_ID;
- static const int32_t DEVICE_GENERATION;
- static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
- static const int32_t EVENTHUB_ID;
-
- std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<FakeInputReaderPolicy> mFakePolicy;
- std::unique_ptr<TestInputListener> mFakeListener;
- std::unique_ptr<InstrumentedInputReader> mReader;
- std::shared_ptr<InputDevice> mDevice;
-
- virtual void SetUp(ftl::Flags<InputDeviceClass> classes) {
- mFakeEventHub = std::make_unique<FakeEventHub>();
- mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakeListener = std::make_unique<TestInputListener>();
- mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- *mFakeListener);
- mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
- // Consume the device reset notification generated when adding a new device.
- mFakeListener->assertNotifyDeviceResetWasCalled();
- }
-
- void SetUp() override {
- SetUp(DEVICE_CLASSES);
- }
-
- void TearDown() override {
- mFakeListener.reset();
- mFakePolicy.clear();
- }
-
- void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
- }
-
- void configureDevice(uint32_t changes) {
- if (!changes ||
- (changes &
- (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
- mReader->requestRefreshConfiguration(changes);
- mReader->loopOnce();
- }
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location, int32_t eventHubId,
- ftl::Flags<InputDeviceClass> classes) {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- std::shared_ptr<InputDevice> device =
- std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
- identifier);
- mReader->pushNextDevice(device);
- mFakeEventHub->addDevice(eventHubId, name, classes);
- mReader->loopOnce();
- return device;
- }
-
- template <class T, typename... Args>
- T& addMapperAndConfigure(Args... args) {
- T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
- configureDevice(0);
- mDevice->reset(ARBITRARY_TIME);
- mapper.reset(ARBITRARY_TIME);
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return mapper;
- }
-
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
- uniqueId, physicalPort, viewportType);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void clearViewports() {
- mFakePolicy->clearViewports();
- }
-
- void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
- int32_t value) {
- RawEvent event;
- event.when = when;
- event.readTime = readTime;
- event.deviceId = mapper.getDeviceContext().getEventHubId();
- event.type = type;
- event.code = code;
- event.value = value;
- mapper.process(&event);
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- }
-
- static 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;
- ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
- }
-
- 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) {
- 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);
- ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
- ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
- ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
- }
-
- static void assertPosition(const FakePointerController& controller, float x, float y) {
- float actualX, actualY;
- controller.getPosition(&actualX, &actualY);
- ASSERT_NEAR(x, actualX, 1);
- ASSERT_NEAR(y, actualY, 1);
- }
-};
-
-const char* InputMapperTest::DEVICE_NAME = "device";
-const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
-const int32_t InputMapperTest::DEVICE_GENERATION = 2;
-const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
- ftl::Flags<InputDeviceClass>(0); // not needed for current tests
-const int32_t InputMapperTest::EVENTHUB_ID = 1;
+TEST_F(InputDeviceTest, GetBluetoothAddress) {
+ const auto& address = mReader->getBluetoothAddress(DEVICE_ID);
+ ASSERT_TRUE(address);
+ ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
+}
// --- SwitchInputMapperTest ---
@@ -3166,14 +2529,17 @@
TEST_F(SwitchInputMapperTest, Process) {
SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+ std::list<NotifyArgs> out;
+ out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
+ ASSERT_TRUE(out.empty());
+ out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+ ASSERT_TRUE(out.empty());
+ out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+ ASSERT_TRUE(out.empty());
+ out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
- NotifySwitchArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
+ ASSERT_EQ(1u, out.size());
+ const NotifySwitchArgs& args = std::get<NotifySwitchArgs>(*out.begin());
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
@@ -3220,22 +2586,23 @@
ASSERT_FALSE(mapper.isVibrating());
// Start vibrating
- mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
+ std::list<NotifyArgs> out = mapper.vibrate(sequence, -1 /* repeat */, VIBRATION_TOKEN);
ASSERT_TRUE(mapper.isVibrating());
// Verify vibrator state listener was notified.
mReader->loopOnce();
- NotifyVibratorStateArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_TRUE(args.isOn);
+ ASSERT_EQ(1u, out.size());
+ const NotifyVibratorStateArgs& vibrateArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+ ASSERT_EQ(DEVICE_ID, vibrateArgs.deviceId);
+ ASSERT_TRUE(vibrateArgs.isOn);
// Stop vibrating
- mapper.cancelVibrate(VIBRATION_TOKEN);
+ out = mapper.cancelVibrate(VIBRATION_TOKEN);
ASSERT_FALSE(mapper.isVibrating());
// Verify vibrator state listener was notified.
mReader->loopOnce();
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyVibratorStateWasCalled(&args));
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_FALSE(args.isOn);
+ ASSERT_EQ(1u, out.size());
+ const NotifyVibratorStateArgs& cancelArgs = std::get<NotifyVibratorStateArgs>(*out.begin());
+ ASSERT_EQ(DEVICE_ID, cancelArgs.deviceId);
+ ASSERT_FALSE(cancelArgs.isOn);
}
// --- SensorInputMapperTest ---
@@ -3397,7 +2764,7 @@
protected:
const std::string UNIQUE_ID = "local:0";
- void prepareDisplay(int32_t orientation);
+ void prepareDisplay(ui::Rotation orientation);
void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
int32_t originalKeyCode, int32_t rotatedKeyCode,
@@ -3407,7 +2774,7 @@
/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
* orientation.
*/
-void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
}
@@ -3545,6 +2912,27 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
}
+TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
+ mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+ // Key down by scan code.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEYCODE_B, args.keyCode);
+
+ // Key up by scan code.
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEYCODE_B, args.keyCode);
+}
+
/**
* Ensure that the readTime is set to the time when the EV_KEY is received.
*/
@@ -3619,7 +3007,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -3641,7 +3029,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -3652,7 +3040,7 @@
AKEYCODE_DPAD_LEFT, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -3663,7 +3051,7 @@
AKEYCODE_DPAD_DOWN, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -3674,7 +3062,7 @@
AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -3688,7 +3076,7 @@
// in the key up as we did in the key down.
NotifyKeyArgs args;
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
@@ -3696,7 +3084,7 @@
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -3721,7 +3109,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
@@ -3743,7 +3131,7 @@
// Display id should be ADISPLAY_ID_NONE without any display configuration.
// ^--- already checked by the previous test
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -3753,7 +3141,7 @@
constexpr int32_t newDisplayId = 2;
clearViewports();
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -3891,7 +3279,7 @@
AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
// Meta state should be AMETA_NONE after reset
- mapper.reset(ARBITRARY_TIME);
+ std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
// Meta state should be AMETA_NONE with update, as device doesn't have the keys.
mapper.updateMetaState(AKEYCODE_NUM_LOCK);
@@ -3943,8 +3331,10 @@
KeyboardInputMapper& mapper2 =
device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
- device2->reset(ARBITRARY_TIME);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ 0 /*changes*/);
+ unused += device2->reset(ARBITRARY_TIME);
// Prepared displays and associated info.
constexpr uint8_t hdmi1 = 0;
@@ -3955,19 +3345,19 @@
mFakePolicy->addInputPortAssociation(USB2, hdmi2);
// No associated display viewport found, should disable the device.
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
ASSERT_FALSE(device2->isEnabled());
// Prepare second display.
constexpr int32_t newDisplayId = 2;
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
// Default device will reconfigure above, need additional reconfiguration for another device.
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
// Device should be enabled after the associated display is found.
ASSERT_TRUE(mDevice->isEnabled());
@@ -4051,8 +3441,10 @@
KeyboardInputMapper& mapper2 =
device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
- device2->reset(ARBITRARY_TIME);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ 0 /*changes*/);
+ unused += device2->reset(ARBITRARY_TIME);
ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
@@ -4110,8 +3502,10 @@
KeyboardInputMapper& mapper2 =
device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
- device2->reset(ARBITRARY_TIME);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ 0 /*changes*/);
+ unused += device2->reset(ARBITRARY_TIME);
// Initial metastate is AMETA_NONE.
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
@@ -4290,14 +3684,14 @@
void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
int32_t rotatedX, int32_t rotatedY);
- void prepareDisplay(int32_t orientation) {
+ void prepareDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
}
void prepareSecondaryDisplay() {
setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+ ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
ViewportType::EXTERNAL);
}
@@ -4582,7 +3976,7 @@
addConfigurationProperty("cursor.orientationAware", "1");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -4601,7 +3995,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -4612,7 +4006,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
@@ -4623,7 +4017,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
@@ -4634,7 +4028,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
@@ -5098,7 +4492,7 @@
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
// Ensure the display is rotated.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
NotifyMotionArgs args;
@@ -5134,7 +4528,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
@@ -5161,7 +4555,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
// Set up the secondary display as the display on which the pointer should be shown,
// and associate the InputDevice with the secondary display.
@@ -5188,7 +4582,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display as the display on which the pointer should be shown.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
// Associate the InputDevice with the secondary display.
@@ -5204,6 +4598,106 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
+// --- BluetoothCursorInputMapperTest ---
+
+class BluetoothCursorInputMapperTest : public CursorInputMapperTest {
+protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+};
+
+TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = addMapperAndConfigure<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))));
+
+ // Process several events that come in quick succession, according to their timestamps.
+ for (int i = 0; i < 3; i++) {
+ constexpr static nsecs_t delta = ms2ns(1);
+ static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
+ kernelEventTime += delta;
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+
+ // Process several events with the same timestamp from the kernel.
+ // Ensure that we do not generate events too far into the future.
+ constexpr static int32_t numEvents =
+ MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
+ for (int i = 0; i < numEvents; i++) {
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+ }
+
+ // By processing more events with the same timestamp, we should not generate events with a
+ // timestamp that is more than the specified max time delta from the timestamp at its injection.
+ const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
+ for (int i = 0; i < 3; i++) {
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(cappedEventTime))));
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) {
+ addConfigurationProperty("cursor.mode", "pointer");
+ CursorInputMapper& mapper = addMapperAndConfigure<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))));
+
+ // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
+ // smoothening is not needed, its timestamp is not affected.
+ kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
+ expectedEventTime = kernelEventTime;
+
+ process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime))));
+}
+
// --- TouchInputMapperTest ---
class TouchInputMapperTest : public InputMapperTest {
@@ -5255,9 +4749,9 @@
TOOL_TYPE = 1 << 10,
};
- void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+ void prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port = NO_PORT);
void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
- void prepareVirtualDisplay(int32_t orientation);
+ void prepareVirtualDisplay(ui::Rotation orientation);
void prepareVirtualKeys();
void prepareLocationCalibration();
int32_t toRawX(float displayX);
@@ -5311,17 +4805,17 @@
{ KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
};
-void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
+void TouchInputMapperTest::prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
port, ViewportType::INTERNAL);
}
void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
+ ui::ROTATION_0, SECONDARY_UNIQUE_ID, port, type);
}
-void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareVirtualDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
ViewportType::VIRTUAL);
@@ -5474,7 +4968,7 @@
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
@@ -5488,7 +4982,7 @@
TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5516,7 +5010,7 @@
TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5544,7 +5038,7 @@
TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5559,7 +5053,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5609,7 +5103,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5730,7 +5224,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5805,7 +5299,7 @@
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -5901,7 +5395,7 @@
TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6000,7 +5494,7 @@
NotifyMotionArgs args;
// Rotation 90.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -6026,7 +5520,7 @@
// Rotation 0.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -6040,7 +5534,7 @@
// Rotation 90.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -6054,7 +5548,7 @@
// Rotation 180.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -6068,7 +5562,7 @@
// Rotation 270.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
processSync(mapper);
@@ -6088,7 +5582,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_0");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6112,7 +5606,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_90");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6136,7 +5630,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_180");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6160,7 +5654,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_270");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6191,7 +5685,7 @@
// Orientation 90, Rotation 0.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
processSync(mapper);
@@ -6205,7 +5699,7 @@
// Orientation 90, Rotation 90.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
processSync(mapper);
@@ -6219,7 +5713,7 @@
// Orientation 90, Rotation 180.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -6233,7 +5727,7 @@
// Orientation 90, Rotation 270.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -6249,7 +5743,7 @@
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6293,7 +5787,7 @@
TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareLocationCalibration();
prepareButtons();
prepareAxes(POSITION);
@@ -6316,7 +5810,7 @@
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6559,7 +6053,7 @@
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6694,7 +6188,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
@@ -6766,7 +6260,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6835,10 +6329,59 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION | PRESSURE);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Touch down.
+ processDown(mapper, 100, 200);
+ processPressure(mapper, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Reset the mapper. This should cancel the ongoing gesture.
+ resetMapper(mapper, ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION | PRESSURE);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Set the initial state for the touch pointer.
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 200);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MAX);
+ mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+
+ // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch
+ // state by reading the current axis values. Since there was no ongoing gesture, calling reset
+ // does not generate any events.
+ resetMapper(mapper, ARBITRARY_TIME);
+
+ // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+ // the recreated touch state to generate a down event.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6866,7 +6409,7 @@
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6894,10 +6437,17 @@
// No events are generated while the viewport is inactive.
processMove(mapper, 101, 201);
processSync(mapper);
- processDown(mapper, 102, 202);
+ processUp(mapper);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ // Start a new gesture while the viewport is still inactive.
+ processDown(mapper, 300, 400);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 300);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 400);
+ mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+ processSync(mapper);
+
// Make the viewport active again. The device should resume processing events.
viewport->isActive = true;
mFakePolicy->updateViewport(*viewport);
@@ -6907,17 +6457,54 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
- // Start a new gesture.
- processDown(mapper, 100, 200);
+ // In the next sync, the touch state that was recreated when the device was reset is reported.
processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
// No more events.
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
}
+TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Press a stylus button.
+ processKey(mapper, BTN_STYLUS, 1);
+ processSync(mapper);
+
+ // Start a touch gesture and ensure the BUTTON_PRESS event is generated.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(toDisplayX(100), toDisplayY(200)),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithCoords(toDisplayX(100), toDisplayY(200)),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ // Release the touch gesture. Ensure that the BUTTON_RELEASE event is generated even though
+ // the button has not actually been released, since there will be no pointers through which the
+ // button state can be reported. The event is generated at the location of the pointer before
+ // it went up.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -6925,27 +6512,25 @@
// The values inside DisplayViewport are expected to be pre-rotated. This updates the current
// DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
// rotated equivalent of the given un-rotated physical display bounds.
- void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+ void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
uint32_t inverseRotationFlags;
auto width = DISPLAY_WIDTH;
auto height = DISPLAY_HEIGHT;
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
inverseRotationFlags = ui::Transform::ROT_270;
std::swap(width, height);
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
inverseRotationFlags = ui::Transform::ROT_180;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
inverseRotationFlags = ui::Transform::ROT_90;
std::swap(width, height);
break;
- case DISPLAY_ORIENTATION_0:
+ case ui::ROTATION_0:
inverseRotationFlags = ui::Transform::ROT_0;
break;
- default:
- FAIL() << "Invalid orientation: " << orientation;
}
const ui::Transform rotation(inverseRotationFlags, width, height);
@@ -6989,7 +6574,7 @@
TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
@@ -7004,8 +6589,7 @@
static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
{{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
- for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
+ for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
configurePhysicalDisplay(orientation, kPhysicalDisplay);
// Touches outside the physical display should be ignored, and should not generate any
@@ -7025,7 +6609,7 @@
TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
@@ -7038,8 +6622,7 @@
// points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
static const Rect kPhysicalDisplay{10, 20, 70, 160};
- for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
+ for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
configurePhysicalDisplay(orientation, kPhysicalDisplay);
// Touches that start outside the physical display should be ignored until it enters the
@@ -7084,6 +6667,360 @@
}
}
+// --- ExternalStylusFusionTest ---
+
+class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
+public:
+ SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ mStylusState.when = ARBITRARY_TIME;
+ mStylusState.pressure = 0.f;
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
+ configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+ processExternalStylusState(mapper);
+ return mapper;
+ }
+
+ std::list<NotifyArgs> processExternalStylusState(InputMapper& mapper) {
+ std::list<NotifyArgs> generatedArgs = mapper.updateExternalStylusState(mStylusState);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+ }
+
+protected:
+ StylusState mStylusState{};
+ static constexpr uint32_t EXPECTED_SOURCE =
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_BLUETOOTH_STYLUS;
+
+ void testStartFusedStylusGesture(SingleTouchInputMapper& mapper) {
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ // The first pointer is withheld.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
+ ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
+
+ // The external stylus reports pressure. The withheld finger pointer is released as a
+ // stylus.
+ mStylusState.pressure = 1.f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // Subsequent pointer events are not withheld.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+
+ void testSuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
+ ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
+
+ // Releasing the touch pointer ends the gesture.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ mStylusState.pressure = 0.f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+
+ void testUnsuccessfulFusionGesture(SingleTouchInputMapper& mapper) {
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER));
+
+ // The first pointer is withheld when an external stylus is connected,
+ // and a timeout is requested.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
+ ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
+
+ // If the timeout expires early, it is requested again.
+ handleTimeout(mapper, ARBITRARY_TIME + 1);
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasRequested(
+ ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT));
+
+ // When the timeout expires, the withheld touch is released as a finger pointer.
+ handleTimeout(mapper, ARBITRARY_TIME + EXTERNAL_STYLUS_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
+
+ // Subsequent pointer events are not withheld.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+
+private:
+ InputDeviceInfo mExternalStylusDeviceInfo{};
+};
+
+TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ ASSERT_EQ(EXPECTED_SOURCE, mapper.getSources());
+}
+
+TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+}
+
+TEST_F(ExternalStylusFusionTest, SuccessfulFusion_TouchFirst) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+}
+
+// Test a successful stylus fusion gesture where the pressure is reported by the external
+// before the touch is reported by the touchscreen.
+TEST_F(ExternalStylusFusionTest, SuccessfulFusion_PressureFirst) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ // The external stylus reports pressure first. It is ignored for now.
+ mStylusState.pressure = 1.f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // When the touch goes down afterwards, it is reported as a stylus pointer.
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(ExternalStylusFusionTest, FusionIsRepeatedForEachNewGesture) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testSuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+ ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
+}
+
+TEST_F(ExternalStylusFusionTest, FusedPointerReportsPressureChanges) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ mStylusState.pressure = 0.8f;
+ processExternalStylusState(mapper);
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithPressure(0.8f))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // The external stylus reports a pressure change. We wait for some time for a touch event.
+ mStylusState.pressure = 0.6f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is reported within the timeout, it reports the updated pressure.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.6f))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // There is another pressure change.
+ mStylusState.pressure = 0.5f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is not reported within the timeout, a move event is generated to report
+ // the new pressure.
+ handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.5f))));
+
+ // If a zero pressure is reported before the touch goes up, the previous pressure value is
+ // repeated indefinitely.
+ mStylusState.pressure = 0.0f;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+ processMove(mapper, 102, 202);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.5f))));
+ processMove(mapper, 103, 203);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithPressure(0.5f))));
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSource(EXPECTED_SOURCE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(ExternalStylusFusionTest, FusedPointerReportsToolTypeChanges) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto source = WithSource(EXPECTED_SOURCE);
+
+ mStylusState.pressure = 1.f;
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_ERASER;
+ processExternalStylusState(mapper);
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_ERASER))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // The external stylus reports a tool change. We wait for some time for a touch event.
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is reported within the timeout, it reports the updated pressure.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // There is another tool type change.
+ mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is not reported within the timeout, a move event is generated to report
+ // the new tool type.
+ handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(source, WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(ExternalStylusFusionTest, FusedPointerReportsButtons) {
+ SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
+ auto toolTypeSource =
+ AllOf(WithSource(EXPECTED_SOURCE), WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS));
+
+ ASSERT_NO_FATAL_FAILURE(testStartFusedStylusGesture(mapper));
+
+ // The external stylus reports a button change. We wait for some time for a touch event.
+ mStylusState.buttons = AMOTION_EVENT_BUTTON_STYLUS_PRIMARY;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is reported within the timeout, it reports the updated button state.
+ processMove(mapper, 101, 201);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+
+ // The button is now released.
+ mStylusState.buttons = 0;
+ processExternalStylusState(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(
+ mReader->getContext()->assertTimeoutWasRequested(ARBITRARY_TIME + TOUCH_DATA_TIMEOUT));
+
+ // If a touch is not reported within the timeout, a move event is generated to report
+ // the new button state.
+ handleTimeout(mapper, ARBITRARY_TIME + TOUCH_DATA_TIMEOUT);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(0))));
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(toolTypeSource, WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertTimeoutWasNotRequested());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- MultiTouchInputMapperTest ---
class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -7102,8 +7039,10 @@
void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+ void processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode, int32_t value);
void processMTSync(MultiTouchInputMapper& mapper);
- void processSync(MultiTouchInputMapper& mapper);
+ void processSync(MultiTouchInputMapper& mapper, nsecs_t eventTime = ARBITRARY_TIME,
+ nsecs_t readTime = READ_TIME);
};
void MultiTouchInputMapperTest::prepareAxes(int axes) {
@@ -7206,17 +7145,24 @@
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
+void MultiTouchInputMapperTest::processHidUsage(MultiTouchInputMapper& mapper, int32_t usageCode,
+ int32_t value) {
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, usageCode);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, value);
+}
+
void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
}
-void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
+void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper, nsecs_t eventTime,
+ nsecs_t readTime) {
+ process(mapper, eventTime, readTime, EV_SYN, SYN_REPORT, 0);
}
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7488,7 +7434,7 @@
TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
/*fuzz*/ 0, /*resolution*/ 10);
@@ -7518,7 +7464,7 @@
TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupported) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
/*fuzz*/ 0, /*resolution*/ 10);
@@ -7539,7 +7485,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7710,7 +7656,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7876,7 +7822,7 @@
TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7925,7 +7871,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | MINOR);
addConfigurationProperty("touch.size.calibration", "geometric");
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -7962,7 +7908,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
addConfigurationProperty("touch.size.calibration", "diameter");
addConfigurationProperty("touch.size.scale", "10");
@@ -8013,7 +7959,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
addConfigurationProperty("touch.size.calibration", "area");
addConfigurationProperty("touch.size.scale", "43");
@@ -8046,7 +7992,7 @@
TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | PRESSURE);
addConfigurationProperty("touch.pressure.calibration", "amplitude");
addConfigurationProperty("touch.pressure.scale", "0.01");
@@ -8080,7 +8026,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8321,9 +8267,66 @@
ASSERT_EQ(0, motionArgs.buttonState);
}
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_STYLUS_BUTTON_PRIMARY, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, 0xabcd, AKEYCODE_STYLUS_BUTTON_SECONDARY, 0);
+
+ // Touch down.
+ processId(mapper, 1);
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithButtonState(0))));
+
+ // Press and release button mapped to the primary stylus button.
+ processKey(mapper, BTN_A, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY))));
+
+ processKey(mapper, BTN_A, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+ // Press and release the HID usage mapped to the secondary stylus button.
+ processHidUsage(mapper, 0xabcd, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_STYLUS_SECONDARY))));
+
+ processHidUsage(mapper, 0xabcd, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithButtonState(0))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithButtonState(0))));
+
+ // Release touch.
+ processId(mapper, -1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0))));
+}
+
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8473,7 +8476,7 @@
TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8544,7 +8547,7 @@
TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8644,7 +8647,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// Add viewport for display 1 on hdmi1
- prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareDisplay(ui::ROTATION_0, hdmi1);
// Send a touch event again
processPosition(mapper, 100, 100);
processSync(mapper);
@@ -8661,8 +8664,8 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
// Send a touch event
processPosition(mapper, 100, 100);
@@ -8685,12 +8688,12 @@
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- // Check source is mouse that would obtain the PointerController.
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+ // Check source is a touchpad that would obtain the PointerController.
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
NotifyMotionArgs motionArgs;
processPosition(mapper, 100, 100);
@@ -8709,7 +8712,7 @@
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1);
process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100);
process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100);
@@ -8733,9 +8736,9 @@
*/
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ // Don't set touch.enableForInactiveViewport to verify the default behavior.
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8747,11 +8750,32 @@
mFakeListener->assertNotifyMotionWasNotCalled();
}
+/**
+ * When the viewport is not active (isActive=false) and touch.enableForInactiveViewport is true,
+ * the touch mapper can process the events and the events can be delivered to the listener.
+ */
+TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.enableForInactiveViewport", "1");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+}
+
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ addConfigurationProperty("touch.enableForInactiveViewport", "0");
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
std::optional<DisplayViewport> optionalDisplayViewport =
mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
ASSERT_TRUE(optionalDisplayViewport.has_value());
@@ -8827,8 +8851,10 @@
// Setup the second touch screen device.
MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID);
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
- device2->reset(ARBITRARY_TIME);
+ std::list<NotifyArgs> unused =
+ device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ 0 /*changes*/);
+ unused += device2->reset(ARBITRARY_TIME);
// Setup PointerController.
std::shared_ptr<FakePointerController> fakePointerController =
@@ -8843,13 +8869,13 @@
mFakePolicy->setShowTouches(true);
// Create displays.
- prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareDisplay(ui::ROTATION_0, hdmi1);
prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
// Default device will reconfigure above, need additional reconfiguration for another device.
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -8879,8 +8905,8 @@
// Disable the show touches configuration and ensure the spots are cleared.
mFakePolicy->setShowTouches(false);
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+ unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
ASSERT_TRUE(fakePointerController->getSpots().empty());
}
@@ -8888,7 +8914,7 @@
TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
NotifyMotionArgs motionArgs;
@@ -8919,8 +8945,7 @@
NotifyMotionArgs motionArgs;
// Test all 4 orientations
- for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
- DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
clearViewports();
prepareDisplay(orientation);
@@ -8945,8 +8970,7 @@
NotifyMotionArgs motionArgs;
// Test all 4 orientations
- for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
- DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
clearViewports();
prepareDisplay(orientation);
@@ -8980,7 +9004,7 @@
std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
NotifyMotionArgs motionArgs;
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
processPosition(mapper, 100, 200);
processSync(mapper);
@@ -9003,7 +9027,7 @@
std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
NotifyMotionArgs motionArgs;
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
processPosition(mapper, 100, 200);
processSync(mapper);
@@ -9013,7 +9037,7 @@
// compared to the display. This is so that when the window transform (which contains the
// display rotation) is applied later by InputDispatcher, the coordinates end up in the
// window's coordinate space.
- frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90));
+ frame.rotate(getInverseRotation(ui::ROTATION_90));
});
ASSERT_EQ(frames, motionArgs.videoFrames);
}
@@ -9050,7 +9074,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9095,7 +9119,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9143,7 +9167,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9218,7 +9242,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9316,7 +9340,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9388,7 +9412,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9443,6 +9467,123 @@
ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
}
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, 300, 400);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN)));
+
+ // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
+ // preserved. Resetting should cancel the ongoing gesture.
+ resetMapper(mapper, ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
+
+ // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+ // the existing touch state to generate a down event.
+ processPosition(mapper, 301, 302);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f))));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // First finger touches down and releases.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+ // Reset the mapper. When the mapper is reset, we expect it to restore the latest
+ // raw state where no pointers are down.
+ resetMapper(mapper, ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Send an empty sync frame. Since there are no pointers, no events are generated.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the
+ // ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source.
+ // Due to limitations in the evdev protocol, we cannot say for certain that a device is capable
+ // of reporting stylus events just because it supports ABS_MT_TOOL_TYPE.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+ // However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be
+ // reported with the stylus source.
+ processId(mapper, FIRST_TRACKING_ID);
+ processToolType(mapper, MT_TOOL_PEN);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+ // Now that we know the device supports styluses, ensure that the device is re-configured with
+ // the stylus source.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, mapper.getSources());
+ {
+ const auto& devices = mReader->getInputDevices();
+ auto deviceInfo =
+ std::find_if(devices.begin(), devices.end(),
+ [](const InputDeviceInfo& info) { return info.getId() == DEVICE_ID; });
+ LOG_ALWAYS_FATAL_IF(deviceInfo == devices.end(), "Cannot find InputDevice");
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, deviceInfo->getSources());
+ }
+
+ // Ensure the device was not reset to prevent interruptions of any ongoing gestures.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
@@ -9456,7 +9597,7 @@
TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
@@ -9487,7 +9628,7 @@
fakePointerController->setButtonState(0);
// prepare device and capture
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -9622,11 +9763,11 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- // non captured touchpad should be a mouse source
+ // A non captured touchpad should have a mouse and touchpad source.
mFakePolicy->setPointerCapture(false);
configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
@@ -9637,7 +9778,7 @@
fakePointerController->setButtonState(0);
// prepare device and capture
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -9678,22 +9819,72 @@
std::shared_ptr<FakePointerController> fakePointerController =
std::make_shared<FakePointerController>();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakePolicy->setPointerController(fakePointerController);
mFakePolicy->setPointerCapture(false);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- // uncaptured touchpad should be a pointer device
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+ // An uncaptured touchpad should be a pointer device, with additional touchpad source.
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
- // captured touchpad should be a touchpad device
+ // A captured touchpad should just have a touchpad source.
mFakePolicy->setPointerCapture(true);
configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
+// --- BluetoothMultiTouchInputMapperTest ---
+
+class BluetoothMultiTouchInputMapperTest : public MultiTouchInputMapperTest {
+protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
+ }
+};
+
+TEST_F(BluetoothMultiTouchInputMapperTest, TimestampSmoothening) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ // Touch down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper, ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithEventTime(ARBITRARY_TIME))));
+
+ // 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;
+
+ processPosition(mapper, 101 + i, 201 + i);
+ processSync(mapper, kernelEventTime);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithEventTime(expectedEventTime))));
+ }
+
+ // Release the touch.
+ processId(mapper, INVALID_TRACKING_ID);
+ processPressure(mapper, RAW_PRESSURE_MIN);
+ processSync(mapper, ARBITRARY_TIME + ms2ns(50));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithEventTime(ARBITRARY_TIME + ms2ns(50)))));
+}
+
+// --- MultiTouchPointerModeTest ---
+
class MultiTouchPointerModeTest : public MultiTouchInputMapperTest {
protected:
float mPointerMovementScale;
@@ -9705,7 +9896,7 @@
fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(0, 0);
fakePointerController->setButtonState(0);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
@@ -9954,6 +10145,92 @@
0, 0, 0, 0, 0));
}
+TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) {
+ preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Place two fingers down.
+ int32_t x1 = 100, y1 = 125, x2 = 550, y2 = 125;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
+ ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET));
+ ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET));
+
+ // Move the two fingers down and to the left.
+ int32_t movingDistance = 200;
+ x1 -= movingDistance;
+ y1 += movingDistance;
+ x2 -= movingDistance;
+ y2 += movingDistance;
+
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(1U, motionArgs.pointerCount);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
+ ASSERT_LT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET), 0);
+ ASSERT_GT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET), 0);
+}
+
+TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
+ preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Start a stylus gesture.
+ processKey(mapper, BTN_TOOL_PEN, 1);
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // TODO(b/257078296): Pointer mode generates extra event.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus
+ // gesture should be disabled.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->isActive = false;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // TODO(b/257078296): Pointer mode generates extra event.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- JoystickInputMapperTest ---
class JoystickInputMapperTest : public InputMapperTest {
@@ -9979,7 +10256,7 @@
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
- void prepareVirtualDisplay(int32_t orientation) {
+ void prepareVirtualDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::VIRTUAL);
@@ -9997,7 +10274,7 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
// Send an axis event
processAxis(mapper, ABS_X, 100);
@@ -10049,12 +10326,12 @@
mFakePolicy.clear();
}
- void configureDevice(uint32_t changes) {
+ std::list<NotifyArgs> configureDevice(uint32_t changes) {
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mReader->requestRefreshConfiguration(changes);
mReader->loopOnce();
}
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ return mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
}
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
@@ -10100,15 +10377,17 @@
TEST_F(BatteryControllerTest, GetBatteryCapacity) {
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
- ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
- ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+ ASSERT_TRUE(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY).value_or(-1),
+ FakeEventHub::BATTERY_CAPACITY);
}
TEST_F(BatteryControllerTest, GetBatteryStatus) {
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
- ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
- ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+ ASSERT_TRUE(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY).value_or(-1),
+ FakeEventHub::BATTERY_STATUS);
}
// --- LightControllerTest ---
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
new file mode 100644
index 0000000..1f8cd12
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "InstrumentedInputReader.h"
+
+namespace android {
+
+InstrumentedInputReader::InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener)
+ : InputReader(eventHub, policy, listener), mFakeContext(this) {}
+
+void InstrumentedInputReader::pushNextDevice(std::shared_ptr<InputDevice> device) {
+ mNextDevices.push(device);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId,
+ const std::string& name,
+ const std::string& location) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ int32_t generation = deviceId + 1;
+ return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
+ return device;
+ }
+ return InputReader::createDeviceLocked(eventHubId, identifier);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
new file mode 100644
index 0000000..7f8d556
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <memory>
+#include <queue>
+#include <string>
+
+#include <InputDevice.h>
+#include <InputReader.h>
+#include <gtest/gtest.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class InstrumentedInputReader : public InputReader {
+public:
+ InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener);
+ virtual ~InstrumentedInputReader() {}
+
+ void pushNextDevice(std::shared_ptr<InputDevice> device);
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location = "");
+
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
+protected:
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier);
+
+ class FakeInputReaderContext : public ContextImpl {
+ public:
+ FakeInputReaderContext(InputReader* reader)
+ : ContextImpl(reader),
+ mGlobalMetaState(0),
+ mUpdateGlobalMetaStateWasCalled(false),
+ mGeneration(1) {}
+
+ virtual ~FakeInputReaderContext() {}
+
+ void assertUpdateGlobalMetaStateWasCalled() {
+ ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+ << "Expected updateGlobalMetaState() to have been called.";
+ mUpdateGlobalMetaStateWasCalled = false;
+ }
+
+ void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+ uint32_t getGeneration() { return mGeneration; }
+
+ void updateGlobalMetaState() override {
+ mUpdateGlobalMetaStateWasCalled = true;
+ ContextImpl::updateGlobalMetaState();
+ }
+
+ int32_t getGlobalMetaState() override {
+ return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+ }
+
+ int32_t bumpGeneration() override {
+ mGeneration = ContextImpl::bumpGeneration();
+ return mGeneration;
+ }
+
+ void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
+
+ void assertTimeoutWasRequested(nsecs_t when) {
+ ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
+ << " but there was no timeout requested.";
+ ASSERT_EQ(when, *mRequestedTimeout);
+ mRequestedTimeout.reset();
+ }
+
+ void assertTimeoutWasNotRequested() {
+ ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
+ " but one was requested at time "
+ << *mRequestedTimeout;
+ }
+
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
+ outDevices = mExternalStylusDevices;
+ }
+
+ void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
+ mExternalStylusDevices = devices;
+ }
+
+ private:
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
+ std::optional<nsecs_t> mRequestedTimeout;
+ std::vector<InputDeviceInfo> mExternalStylusDevices;
+ } mFakeContext;
+
+ friend class InputReaderTest;
+
+public:
+ FakeInputReaderContext* getContext() { return &mFakeContext; }
+
+private:
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/NotifyArgs_test.cpp b/services/inputflinger/tests/NotifyArgs_test.cpp
new file mode 100644
index 0000000..6715585
--- /dev/null
+++ b/services/inputflinger/tests/NotifyArgs_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 <NotifyArgs.h>
+#include <utils/Timers.h>
+
+#include <gtest/gtest.h>
+#include "android/input.h"
+#include "input/Input.h"
+#include "input/TouchVideoFrame.h"
+
+namespace android {
+
+// --- NotifyArgsTest ---
+
+/**
+ * Validate basic copy assignment.
+ */
+TEST(NotifyMotionArgsTest, TestCopyAssignmentOperator) {
+ int32_t id = 123;
+ nsecs_t downTime = systemTime();
+ nsecs_t eventTime = downTime++;
+ nsecs_t readTime = downTime++;
+ int32_t deviceId = 7;
+ uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ int32_t displayId = 42;
+ uint32_t policyFlags = POLICY_FLAG_GESTURE;
+ int32_t action = AMOTION_EVENT_ACTION_HOVER_MOVE;
+ int32_t actionButton = AMOTION_EVENT_BUTTON_PRIMARY;
+ int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ int32_t metaState = AMETA_SCROLL_LOCK_ON;
+ uint32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY;
+ MotionClassification classification = MotionClassification::DEEP_PRESS;
+ int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+ uint32_t pointerCount = 2;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ float x = 0;
+ float y = 10;
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[i].clear();
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x++);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y++);
+ }
+
+ float xPrecision = 1.2f;
+ float yPrecision = 3.4f;
+ float xCursorPosition = 5.6f;
+ float yCursorPosition = 7.8f;
+
+ std::vector<int16_t> videoData = {1, 2, 3, 4};
+ timeval timestamp = {5, 6};
+ TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+ std::vector<TouchVideoFrame> videoFrames = {frame};
+ const NotifyMotionArgs args(id, eventTime, readTime, deviceId, source, displayId, policyFlags,
+ action, actionButton, flags, metaState, buttonState, classification,
+ edgeFlags, pointerCount, pointerProperties, pointerCoords,
+ xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+ videoFrames);
+
+ NotifyMotionArgs otherArgs{};
+ otherArgs = args;
+
+ EXPECT_EQ(args, otherArgs);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
index 8e2ab88..bd05360 100644
--- a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
+++ b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
@@ -33,14 +33,8 @@
constexpr int32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
constexpr int32_t STYLUS = AINPUT_SOURCE_STYLUS;
-struct PointerData {
- float x;
- float y;
-};
-
static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action,
- const std::vector<PointerData>& points,
- uint32_t source) {
+ const std::vector<Point>& points, uint32_t source) {
size_t pointerCount = points.size();
if (action == DOWN || action == UP) {
EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
new file mode 100644
index 0000000..27881f6
--- /dev/null
+++ b/services/inputflinger/tests/TestConstants.h
@@ -0,0 +1,33 @@
+/*
+ * 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
+
+namespace android {
+
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
+// An arbitrary time value.
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
+
+// Error tolerance for floating point assertions.
+static const float EPSILON = 0.001f;
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 57b382c..2801072 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -59,26 +59,34 @@
assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
}
+void TestInputListener::assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher) {
+ NotifyKeyArgs outEventArgs;
+ ASSERT_NO_FATAL_FAILURE(assertNotifyKeyWasCalled(&outEventArgs));
+ ASSERT_THAT(outEventArgs, matcher);
+}
+
void TestInputListener::assertNotifyKeyWasNotCalled() {
ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}
-void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs,
+ std::optional<TimePoint> waitUntil) {
ASSERT_NO_FATAL_FAILURE(
assertCalled<NotifyMotionArgs>(outEventArgs,
- "Expected notifyMotion() to have been called."));
+ "Expected notifyMotion() to have been called.",
+ waitUntil));
}
void TestInputListener::assertNotifyMotionWasCalled(
- const ::testing::Matcher<NotifyMotionArgs>& matcher) {
+ const ::testing::Matcher<NotifyMotionArgs>& matcher, std::optional<TimePoint> waitUntil) {
NotifyMotionArgs outEventArgs;
- ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs));
+ ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs, waitUntil));
ASSERT_THAT(outEventArgs, matcher);
}
-void TestInputListener::assertNotifyMotionWasNotCalled() {
+void TestInputListener::assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil) {
ASSERT_NO_FATAL_FAILURE(
- assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called."));
+ assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.", waitUntil));
}
void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
@@ -113,15 +121,18 @@
}
template <class NotifyArgsType>
-void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message,
+ std::optional<TimePoint> waitUntil) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
if (queue.empty()) {
- const bool eventReceived =
- mCondition.wait_for(lock, mEventHappenedTimeout,
- [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ const auto time =
+ waitUntil.value_or(std::chrono::system_clock::now() + mEventHappenedTimeout);
+ const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) {
+ return !queue.empty();
+ });
if (!eventReceived) {
FAIL() << "Timed out waiting for event: " << message.c_str();
}
@@ -133,21 +144,23 @@
}
template <class NotifyArgsType>
-void TestInputListener::assertNotCalled(std::string message) {
+void TestInputListener::assertNotCalled(std::string message, std::optional<TimePoint> waitUntil) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
- const bool eventReceived =
- mCondition.wait_for(lock, mEventDidNotHappenTimeout,
- [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ const auto time =
+ waitUntil.value_or(std::chrono::system_clock::now() + mEventDidNotHappenTimeout);
+ const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) {
+ return !queue.empty();
+ });
if (eventReceived) {
FAIL() << "Unexpected event: " << message.c_str();
}
}
template <class NotifyArgsType>
-void TestInputListener::notify(const NotifyArgsType* args) {
+void TestInputListener::addToQueue(const NotifyArgsType* args) {
std::scoped_lock<std::mutex> lock(mLock);
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
@@ -156,35 +169,35 @@
}
void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
- notify<NotifyConfigurationChangedArgs>(args);
+ addToQueue<NotifyConfigurationChangedArgs>(args);
}
void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
- notify<NotifyDeviceResetArgs>(args);
+ addToQueue<NotifyDeviceResetArgs>(args);
}
void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
- notify<NotifyKeyArgs>(args);
+ addToQueue<NotifyKeyArgs>(args);
}
void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
- notify<NotifyMotionArgs>(args);
+ addToQueue<NotifyMotionArgs>(args);
}
void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
- notify<NotifySwitchArgs>(args);
+ addToQueue<NotifySwitchArgs>(args);
}
void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
- notify<NotifyPointerCaptureChangedArgs>(args);
+ addToQueue<NotifyPointerCaptureChangedArgs>(args);
}
void TestInputListener::notifySensor(const NotifySensorArgs* args) {
- notify<NotifySensorArgs>(args);
+ addToQueue<NotifySensorArgs>(args);
}
void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) {
- notify<NotifyVibratorStateArgs>(args);
+ addToQueue<NotifyVibratorStateArgs>(args);
}
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index cad698f..9665f70 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -33,6 +33,8 @@
std::chrono::milliseconds eventDidNotHappenTimeout = 0ms);
virtual ~TestInputListener();
+ using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
+
void assertNotifyConfigurationChangedWasCalled(
NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -44,13 +46,17 @@
void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
+ void assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher);
+
void assertNotifyKeyWasNotCalled();
- void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+ void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr,
+ std::optional<TimePoint> waitUntil = {});
- void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher);
+ void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher,
+ std::optional<TimePoint> waitUntil = {});
- void assertNotifyMotionWasNotCalled();
+ void assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil = {});
void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
@@ -61,13 +67,14 @@
private:
template <class NotifyArgsType>
- void assertCalled(NotifyArgsType* outEventArgs, std::string message);
+ void assertCalled(NotifyArgsType* outEventArgs, std::string message,
+ std::optional<TimePoint> waitUntil = {});
template <class NotifyArgsType>
- void assertNotCalled(std::string message);
+ void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {});
template <class NotifyArgsType>
- void notify(const NotifyArgsType* args);
+ void addToQueue(const NotifyArgsType* args);
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 03736a3..9db3422 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -19,21 +19,35 @@
#include <android/input.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <input/Input.h>
namespace android {
-MATCHER_P(WithMotionAction, action, "InputEvent with specified action") {
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
- return (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
+ bool matches = action == arg.action;
+ if (!matches) {
+ *result_listener << "expected action " << MotionEvent::actionToString(action)
+ << ", but got " << MotionEvent::actionToString(arg.action);
}
- *result_listener << "expected action " << MotionEvent::actionToString(action) << ", but got "
- << MotionEvent::actionToString(arg.action);
- return action == arg.action;
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+ matches &= (arg.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+}
+
+MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
+ *result_listener << "expected action " << KeyEvent::actionToString(action) << ", but got "
+ << KeyEvent::actionToString(arg.action);
+ return arg.action == action;
}
MATCHER_P(WithSource, source, "InputEvent with specified source") {
- *result_listener << "expected source " << source << ", but got " << arg.source;
+ *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+ << inputEventSourceToString(arg.source);
return arg.source == source;
}
@@ -42,6 +56,16 @@
return arg.displayId == displayId;
}
+MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") {
+ *result_listener << "expected deviceId " << deviceId << ", but got " << arg.deviceId;
+ return arg.deviceId == deviceId;
+}
+
+MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
+ *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
+ return arg.keyCode == keyCode;
+}
+
MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
const auto argX = arg.pointerCoords[0].getX();
const auto argY = arg.pointerCoords[0].getY();
@@ -50,9 +74,32 @@
return argX == x && argY == y;
}
+MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
+ const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
+ return argPressure == pressure;
+}
+
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+ const auto argToolType = arg.pointerProperties[0].toolType;
+ *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+ << motionToolTypeToString(argToolType);
+ return argToolType == toolType;
+}
+
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
*result_listener << "expected flags " << flags << ", but got " << arg.flags;
return arg.flags == flags;
}
+MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
+ *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
+ return arg.buttonState == buttons;
+}
+
+MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
+ *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
+ return arg.eventTime == eventTime;
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 9c93919..97a2614 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -17,13 +17,15 @@
#include "UinputDevice.h"
#include <android-base/stringprintf.h>
+#include <cutils/memory.h>
#include <fcntl.h>
namespace android {
// --- UinputDevice ---
-UinputDevice::UinputDevice(const char* name) : mName(name) {}
+UinputDevice::UinputDevice(const char* name, int16_t productId)
+ : mName(name), mProductId(productId) {}
UinputDevice::~UinputDevice() {
if (ioctl(mDeviceFd, UI_DEV_DESTROY)) {
@@ -42,7 +44,7 @@
strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE);
device.id.bustype = BUS_USB;
device.id.vendor = 0x01;
- device.id.product = 0x01;
+ device.id.product = mProductId;
device.id.version = 1;
ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device));
@@ -58,11 +60,11 @@
}
void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+ // uinput ignores the timestamp
struct input_event event = {};
event.type = type;
event.code = code;
event.value = value;
- event.time = {}; // uinput ignores the timestamp
if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16
@@ -75,8 +77,8 @@
// --- UinputKeyboard ---
-UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
- : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}
+UinputKeyboard::UinputKeyboard(const char* name, int16_t productId, std::initializer_list<int> keys)
+ : UinputDevice(name, productId), mKeys(keys.begin(), keys.end()) {}
void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
// enable key press/release event
@@ -120,22 +122,52 @@
// --- UinputHomeKey ---
-UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
+UinputHomeKey::UinputHomeKey() : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {KEY_HOME}) {}
void UinputHomeKey::pressAndReleaseHomeKey() {
pressAndReleaseKey(KEY_HOME);
}
-// --- UinputSteamController
-UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+// --- UinputSteamController ---
+
+UinputSteamController::UinputSteamController()
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+
+// --- UinputExternalStylus ---
+
+UinputExternalStylus::UinputExternalStylus()
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {}
+
+// --- UinputExternalStylusWithPressure ---
+
+UinputExternalStylusWithPressure::UinputExternalStylusWithPressure()
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}) {}
+
+void UinputExternalStylusWithPressure::configureDevice(int fd, uinput_user_dev* device) {
+ UinputKeyboard::configureDevice(fd, device);
+
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
+ device->absmin[ABS_PRESSURE] = RAW_PRESSURE_MIN;
+ device->absmax[ABS_PRESSURE] = RAW_PRESSURE_MAX;
+}
+
+void UinputExternalStylusWithPressure::setPressure(int32_t pressure) {
+ injectEvent(EV_ABS, ABS_PRESSURE, pressure);
+ injectEvent(EV_SYN, SYN_REPORT, 0);
+}
// --- UinputTouchScreen ---
-UinputTouchScreen::UinputTouchScreen(const Rect* size)
- : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
+
+UinputTouchScreen::UinputTouchScreen(const Rect& size)
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID,
+ {BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}),
+ mSize(size) {}
void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
+ UinputKeyboard::configureDevice(fd, device);
+
// Setup the touch screen device
- ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
@@ -145,7 +177,6 @@
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
@@ -157,6 +188,8 @@
device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+ device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
+ device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
}
void UinputTouchScreen::sendSlot(int32_t slot) {
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index e0ff8c3..51e331d 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -32,7 +32,7 @@
template <class D, class... Ts>
std::unique_ptr<D> createUinputDevice(Ts... args) {
// Using `new` to access non-public constructors.
- std::unique_ptr<D> dev(new D(&args...));
+ std::unique_ptr<D> dev(new D(args...));
EXPECT_NO_FATAL_FAILURE(dev->init());
return dev;
}
@@ -51,8 +51,9 @@
protected:
const char* mName;
+ const int16_t mProductId;
- UinputDevice(const char* name);
+ explicit UinputDevice(const char* name, int16_t productId);
// Signals which types of events this device supports before it is created.
// This must be overridden by subclasses.
@@ -71,7 +72,8 @@
class UinputKeyboard : public UinputDevice {
public:
- static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device";
+ static constexpr const char* KEYBOARD_NAME = "Test Uinput Keyboard Device";
+ static constexpr int16_t PRODUCT_ID = 42;
// Injects key press and sync.
void pressKey(int key);
@@ -84,11 +86,12 @@
friend std::unique_ptr<D> createUinputDevice(Ts... args);
protected:
- UinputKeyboard(std::initializer_list<int> keys = {});
+ explicit UinputKeyboard(const char* name, int16_t productId = PRODUCT_ID,
+ std::initializer_list<int> keys = {});
-private:
void configureDevice(int fd, uinput_user_dev* device) override;
+private:
std::set<int> mKeys;
};
@@ -97,6 +100,9 @@
// A keyboard device that has a single HOME key.
class UinputHomeKey : public UinputKeyboard {
public:
+ static constexpr const char* DEVICE_NAME = "Test Uinput Home Key";
+ static constexpr int16_t PRODUCT_ID = 43;
+
// Injects 4 events: key press, sync, key release, and sync.
void pressAndReleaseHomeKey();
@@ -104,24 +110,69 @@
friend std::unique_ptr<D> createUinputDevice(Ts... args);
private:
- UinputHomeKey();
+ explicit UinputHomeKey();
};
+// --- UinputSteamController ---
+
// A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key.
class UinputSteamController : public UinputKeyboard {
public:
+ static constexpr const char* DEVICE_NAME = "Test Uinput Steam Controller";
+ static constexpr int16_t PRODUCT_ID = 44;
+
template <class D, class... Ts>
friend std::unique_ptr<D> createUinputDevice(Ts... args);
private:
- UinputSteamController();
+ explicit UinputSteamController();
+};
+
+// --- UinputExternalStylus ---
+
+// A stylus that reports button presses.
+class UinputExternalStylus : public UinputKeyboard {
+public:
+ static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus";
+ static constexpr int16_t PRODUCT_ID = 45;
+
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+ explicit UinputExternalStylus();
+};
+
+// --- UinputExternalStylusWithPressure ---
+
+// A stylus that reports button presses and pressure values.
+class UinputExternalStylusWithPressure : public UinputKeyboard {
+public:
+ static constexpr const char* DEVICE_NAME = "Test Uinput External Stylus With Pressure";
+ static constexpr int16_t PRODUCT_ID = 46;
+
+ static constexpr int32_t RAW_PRESSURE_MIN = 0;
+ static constexpr int32_t RAW_PRESSURE_MAX = 255;
+
+ void setPressure(int32_t pressure);
+
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+ void configureDevice(int fd, uinput_user_dev* device) override;
+
+ explicit UinputExternalStylusWithPressure();
};
// --- UinputTouchScreen ---
-// A touch screen device with specific size.
-class UinputTouchScreen : public UinputDevice {
+
+// A multi-touch touchscreen device with specific size that also supports styluses.
+class UinputTouchScreen : public UinputKeyboard {
public:
- static constexpr const char* DEVICE_NAME = "Test Touch Screen";
+ static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen";
+ static constexpr int16_t PRODUCT_ID = 47;
+
static const int32_t RAW_TOUCH_MIN = 0;
static const int32_t RAW_TOUCH_MAX = 31;
static const int32_t RAW_ID_MIN = 0;
@@ -146,7 +197,7 @@
const Point getCenterPoint();
protected:
- UinputTouchScreen(const Rect* size);
+ explicit UinputTouchScreen(const Rect& size);
private:
void configureDevice(int fd, uinput_user_dev* device) override;
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 4b542aa..cc523e1 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -51,12 +51,14 @@
},
[&]() -> void { mapper.getSources(); },
[&]() -> void {
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
- fdp->ConsumeIntegral<int32_t>());
+ std::list<NotifyArgs> unused =
+ mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+ fdp->ConsumeIntegral<int32_t>());
},
[&]() -> void {
// Need to reconfigure with 0 or you risk a NPE.
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+ std::list<NotifyArgs> unused =
+ mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
InputDeviceInfo info;
mapper.populateDeviceInfo(&info);
},
@@ -68,23 +70,27 @@
: fdp->ConsumeIntegral<int32_t>();
// Need to reconfigure with 0 or you risk a NPE.
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+ std::list<NotifyArgs> unused =
+ mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
fdp->ConsumeIntegral<nsecs_t>(),
fdp->ConsumeIntegral<int32_t>(),
type,
code,
fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
+ unused += mapper.process(&rawEvent);
},
- [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+ [&]() -> void {
+ std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+ },
[&]() -> void {
mapper.getScanCodeState(fdp->ConsumeIntegral<uint32_t>(),
fdp->ConsumeIntegral<int32_t>());
},
[&]() -> void {
// Need to reconfigure with 0 or you risk a NPE.
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
+ std::list<NotifyArgs> unused =
+ mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig, 0);
mapper.getAssociatedDisplayId();
},
})();
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index 62615d0..1e0764f 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -27,7 +27,7 @@
class FuzzContainer {
std::shared_ptr<FuzzEventHub> mFuzzEventHub;
sp<FuzzInputReaderPolicy> mFuzzPolicy;
- std::unique_ptr<FuzzInputListener> mFuzzListener;
+ FuzzInputListener mFuzzListener;
std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
std::unique_ptr<InputDevice> mFuzzDevice;
InputReaderConfiguration mPolicyConfig;
@@ -44,9 +44,8 @@
// Create mocked objects.
mFuzzEventHub = std::make_shared<FuzzEventHub>(mFdp);
mFuzzPolicy = sp<FuzzInputReaderPolicy>::make(mFdp);
- mFuzzListener = std::make_unique<FuzzInputListener>();
mFuzzContext = std::make_unique<FuzzInputReaderContext>(mFuzzEventHub, mFuzzPolicy,
- *mFuzzListener, mFdp);
+ mFuzzListener, mFdp);
InputDeviceIdentifier identifier;
identifier.name = deviceName;
@@ -60,8 +59,12 @@
void configureDevice() {
nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
- mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
- mFuzzDevice->reset(arbitraryTime);
+ std::list<NotifyArgs> out;
+ out += mFuzzDevice->configure(arbitraryTime, &mPolicyConfig, 0);
+ out += mFuzzDevice->reset(arbitraryTime);
+ for (const NotifyArgs& args : out) {
+ mFuzzListener.notify(args);
+ }
}
void addProperty(std::string key, std::string value) {
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index a9f5a3a..057c15d 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -153,10 +153,18 @@
return reader->getLightPlayerId(deviceId, lightId);
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
+ }
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
+ std::optional<std::string> getBluetoothAddress(int32_t deviceId) const {
+ return reader->getBluetoothAddress(deviceId);
+ }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};
@@ -273,6 +281,7 @@
std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()),
std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
},
+ [&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); },
})();
}
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index c48a099..e880f55 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -63,10 +63,13 @@
},
[&]() -> void { mapper.getSources(); },
[&]() -> void {
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
- fdp->ConsumeIntegral<uint32_t>());
+ std::list<NotifyArgs> unused =
+ mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+ fdp->ConsumeIntegral<uint32_t>());
},
- [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+ [&]() -> void {
+ std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+ },
[&]() -> void {
int32_t type, code;
type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
@@ -79,7 +82,7 @@
type,
code,
fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
+ std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 03c2266..cd852d6 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -211,6 +211,7 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
@@ -311,10 +312,11 @@
return mFdp->ConsumeRandomLengthString(32);
}
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) override {
+ ui::Rotation surfaceRotation) override {
return mTransform;
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
+ void notifyStylusGestureStarted(int32_t, nsecs_t) {}
};
class FuzzInputListener : public virtual InputListenerInterface {
@@ -332,7 +334,6 @@
class FuzzInputReaderContext : public InputReaderContext {
std::shared_ptr<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
- InputListenerInterface& mListener;
std::shared_ptr<FuzzedDataProvider> mFdp;
public:
@@ -340,7 +341,7 @@
const sp<InputReaderPolicyInterface>& policy,
InputListenerInterface& listener,
std::shared_ptr<FuzzedDataProvider> mFdp)
- : mEventHub(eventHub), mPolicy(policy), mListener(listener), mFdp(mFdp) {}
+ : mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {}
~FuzzInputReaderContext() {}
void updateGlobalMetaState() override {}
int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
@@ -355,14 +356,16 @@
void requestTimeoutAtTime(nsecs_t when) override {}
int32_t bumpGeneration() override { return mFdp->ConsumeIntegral<int32_t>(); }
void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {}
- void dispatchExternalStylusState(const StylusState& outState) override {}
+ std::list<NotifyArgs> dispatchExternalStylusState(const StylusState& outState) override {
+ return {};
+ }
InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); }
- InputListenerInterface& getListener() override { return mListener; }
EventHubInterface* getEventHub() override { return mEventHub.get(); }
int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); }
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
+ void notifyStylusGestureStarted(int32_t, nsecs_t) {}
};
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 59b0642..99fd083 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -78,10 +78,13 @@
},
[&]() -> void { mapper.getSources(); },
[&]() -> void {
- mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
- fdp->ConsumeIntegral<uint32_t>());
+ std::list<NotifyArgs> unused =
+ mapper.configure(fdp->ConsumeIntegral<nsecs_t>(), &policyConfig,
+ fdp->ConsumeIntegral<uint32_t>());
},
- [&]() -> void { mapper.reset(fdp->ConsumeIntegral<nsecs_t>()); },
+ [&]() -> void {
+ std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
+ },
[&]() -> void {
int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
: fdp->ConsumeIntegral<int32_t>();
@@ -93,7 +96,7 @@
type,
code,
fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
+ std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
@@ -113,16 +116,20 @@
nullptr);
},
[&]() -> void {
- mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(),
- fdp->ConsumeIntegral<nsecs_t>());
+ std::list<NotifyArgs> unused =
+ mapper.cancelTouch(fdp->ConsumeIntegral<nsecs_t>(),
+ fdp->ConsumeIntegral<nsecs_t>());
},
- [&]() -> void { mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>()); },
+ [&]() -> void {
+ std::list<NotifyArgs> unused =
+ mapper.timeoutExpired(fdp->ConsumeIntegral<nsecs_t>());
+ },
[&]() -> void {
StylusState state{fdp->ConsumeIntegral<nsecs_t>(),
fdp->ConsumeFloatingPoint<float>(),
fdp->ConsumeIntegral<uint32_t>(),
fdp->ConsumeIntegral<int32_t>()};
- mapper.updateExternalStylusState(state);
+ std::list<NotifyArgs> unused = mapper.updateExternalStylusState(state);
},
[&]() -> void { mapper.getAssociatedDisplayId(); },
})();
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index e76bd72..7416ce9 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -46,7 +46,7 @@
type,
code,
fdp->ConsumeIntegral<int32_t>()};
- mapper.process(&rawEvent);
+ std::list<NotifyArgs> unused = mapper.process(&rawEvent);
},
[&]() -> void {
mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 6fbba3f..7fb33e5 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -38,7 +38,9 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V3-cpp",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
+ "android.hardware.power-V4-cpp",
],
cflags: [
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 8c225d5..f89035f 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -33,16 +33,20 @@
// -------------------------------------------------------------------------------------------------
std::unique_ptr<HalWrapper> HalConnector::connect() {
- sp<IPower> halAidl = PowerHalLoader::loadAidl();
- if (halAidl) {
+ if (sp<IPower> halAidl = PowerHalLoader::loadAidl()) {
return std::make_unique<AidlHalWrapper>(halAidl);
}
- sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
- sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
- if (halHidlV1_1) {
- return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
- }
- if (halHidlV1_0) {
+ // If V1_0 isn't defined, none of them are
+ if (sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0()) {
+ if (sp<V1_3::IPower> halHidlV1_3 = PowerHalLoader::loadHidlV1_3()) {
+ return std::make_unique<HidlHalWrapperV1_3>(halHidlV1_3);
+ }
+ if (sp<V1_2::IPower> halHidlV1_2 = PowerHalLoader::loadHidlV1_2()) {
+ return std::make_unique<HidlHalWrapperV1_2>(halHidlV1_2);
+ }
+ if (sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1()) {
+ return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_1);
+ }
return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0);
}
return nullptr;
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 1f1b43a..6bd40f8 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "PowerHalLoader"
#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
#include <binder/IServiceManager.h>
#include <hardware/power.h>
@@ -55,12 +57,16 @@
sp<IPower> PowerHalLoader::gHalAidl = nullptr;
sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr;
sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
+sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
+sp<V1_3::IPower> PowerHalLoader::gHalHidlV1_3 = nullptr;
void PowerHalLoader::unloadAll() {
std::lock_guard<std::mutex> lock(gHalMutex);
gHalAidl = nullptr;
gHalHidlV1_0 = nullptr;
gHalHidlV1_1 = nullptr;
+ gHalHidlV1_2 = nullptr;
+ gHalHidlV1_3 = nullptr;
}
sp<IPower> PowerHalLoader::loadAidl() {
@@ -82,6 +88,20 @@
return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1");
}
+sp<V1_2::IPower> PowerHalLoader::loadHidlV1_2() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_2::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_2::IPower>(gHalExists, gHalHidlV1_2, loadFn, "HIDL v1.2");
+}
+
+sp<V1_3::IPower> PowerHalLoader::loadHidlV1_3() {
+ std::lock_guard<std::mutex> lock(gHalMutex);
+ static bool gHalExists = true;
+ static auto loadFn = []() { return V1_3::IPower::castFrom(loadHidlV1_0Locked()); };
+ return loadHal<V1_3::IPower>(gHalExists, gHalHidlV1_3, loadFn, "HIDL v1.3");
+}
+
sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() {
static bool gHalExists = true;
static auto loadFn = []() { return V1_0::IPower::getService(); };
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index d74bd23..9e7adf8 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -24,8 +24,6 @@
#include <cinttypes>
using namespace android::hardware::power;
-namespace V1_0 = android::hardware::power::V1_0;
-namespace V1_1 = android::hardware::power::V1_1;
namespace Aidl = android::hardware::power;
namespace android {
@@ -108,7 +106,7 @@
HalResult<void> HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) {
if (boost == Boost::INTERACTION) {
- return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs);
+ return sendPowerHint(V1_3::PowerHint::INTERACTION, durationMs);
} else {
ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str());
return HalResult<void>::unsupported();
@@ -119,13 +117,13 @@
uint32_t data = enabled ? 1 : 0;
switch (mode) {
case Mode::LAUNCH:
- return sendPowerHint(V1_0::PowerHint::LAUNCH, data);
+ return sendPowerHint(V1_3::PowerHint::LAUNCH, data);
case Mode::LOW_POWER:
- return sendPowerHint(V1_0::PowerHint::LOW_POWER, data);
+ return sendPowerHint(V1_3::PowerHint::LOW_POWER, data);
case Mode::SUSTAINED_PERFORMANCE:
- return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data);
+ return sendPowerHint(V1_3::PowerHint::SUSTAINED_PERFORMANCE, data);
case Mode::VR:
- return sendPowerHint(V1_0::PowerHint::VR_MODE, data);
+ return sendPowerHint(V1_3::PowerHint::VR_MODE, data);
case Mode::INTERACTIVE:
return setInteractive(enabled);
case Mode::DOUBLE_TAP_TO_WAKE:
@@ -137,8 +135,8 @@
}
}
-HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
- auto ret = mHandleV1_0->powerHint(hintId, data);
+HalResult<void> HidlHalWrapperV1_0::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto ret = mHandleV1_0->powerHint(static_cast<V1_0::PowerHint>(hintId), data);
return HalResult<void>::fromReturn(ret);
}
@@ -152,7 +150,7 @@
return HalResult<void>::fromReturn(ret);
}
-HalResult<sp<Aidl::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
+HalResult<sp<hardware::power::IPowerHintSession>> HidlHalWrapperV1_0::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because Power HAL not available",
threadIds.size());
@@ -166,8 +164,59 @@
// -------------------------------------------------------------------------------------------------
-HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) {
- auto ret = mHandleV1_1->powerHintAsync(hintId, data);
+HalResult<void> HidlHalWrapperV1_1::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto handle = static_cast<V1_1::IPower*>(mHandleV1_0.get());
+ auto ret = handle->powerHintAsync(static_cast<V1_0::PowerHint>(hintId), data);
+ return HalResult<void>::fromReturn(ret);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_2::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto handle = static_cast<V1_2::IPower*>(mHandleV1_0.get());
+ auto ret = handle->powerHintAsync_1_2(static_cast<V1_2::PowerHint>(hintId), data);
+ return HalResult<void>::fromReturn(ret);
+}
+
+HalResult<void> HidlHalWrapperV1_2::setBoost(Boost boost, int32_t durationMs) {
+ switch (boost) {
+ case Boost::CAMERA_SHOT:
+ return sendPowerHint(V1_3::PowerHint::CAMERA_SHOT, durationMs);
+ case Boost::CAMERA_LAUNCH:
+ return sendPowerHint(V1_3::PowerHint::CAMERA_LAUNCH, durationMs);
+ default:
+ return HidlHalWrapperV1_1::setBoost(boost, durationMs);
+ }
+}
+
+HalResult<void> HidlHalWrapperV1_2::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ switch (mode) {
+ case Mode::CAMERA_STREAMING_SECURE:
+ case Mode::CAMERA_STREAMING_LOW:
+ case Mode::CAMERA_STREAMING_MID:
+ case Mode::CAMERA_STREAMING_HIGH:
+ return sendPowerHint(V1_3::PowerHint::CAMERA_STREAMING, data);
+ case Mode::AUDIO_STREAMING_LOW_LATENCY:
+ return sendPowerHint(V1_3::PowerHint::AUDIO_LOW_LATENCY, data);
+ default:
+ return HidlHalWrapperV1_1::setMode(mode, enabled);
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+
+HalResult<void> HidlHalWrapperV1_3::setMode(Mode mode, bool enabled) {
+ uint32_t data = enabled ? 1 : 0;
+ if (mode == Mode::EXPENSIVE_RENDERING) {
+ return sendPowerHint(V1_3::PowerHint::EXPENSIVE_RENDERING, data);
+ }
+ return HidlHalWrapperV1_2::setMode(mode, enabled);
+}
+
+HalResult<void> HidlHalWrapperV1_3::sendPowerHint(V1_3::PowerHint hintId, uint32_t data) {
+ auto handle = static_cast<V1_3::IPower*>(mHandleV1_0.get());
+ auto ret = handle->powerHintAsync_1_3(hintId, data);
return HalResult<void>::fromReturn(ret);
}
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index fcb012f..4343aec 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -38,7 +38,9 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V3-cpp",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
+ "android.hardware.power-V4-cpp",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 962784c..54dffcf 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -31,6 +31,8 @@
"PowerHalWrapperAidlTest.cpp",
"PowerHalWrapperHidlV1_0Test.cpp",
"PowerHalWrapperHidlV1_1Test.cpp",
+ "PowerHalWrapperHidlV1_2Test.cpp",
+ "PowerHalWrapperHidlV1_3Test.cpp",
"WorkSourceTest.cpp",
],
cflags: [
@@ -47,7 +49,9 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V3-cpp",
+ "android.hardware.power@1.2",
+ "android.hardware.power@1.3",
+ "android.hardware.power-V4-cpp",
],
static_libs: [
"libgmock",
diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp
index 058e1b5..e36deed 100644
--- a/services/powermanager/tests/PowerHalLoaderTest.cpp
+++ b/services/powermanager/tests/PowerHalLoaderTest.cpp
@@ -26,6 +26,8 @@
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
+using IPowerV1_2 = android::hardware::power::V1_2::IPower;
+using IPowerV1_3 = android::hardware::power::V1_3::IPower;
using IPowerAidl = android::hardware::power::IPower;
using namespace android;
@@ -52,6 +54,16 @@
return PowerHalLoader::loadHidlV1_1();
}
+template <>
+sp<IPowerV1_2> loadHal<IPowerV1_2>() {
+ return PowerHalLoader::loadHidlV1_2();
+}
+
+template <>
+sp<IPowerV1_3> loadHal<IPowerV1_3>() {
+ return PowerHalLoader::loadHidlV1_3();
+}
+
// -------------------------------------------------------------------------------------------------
template <typename T>
@@ -63,7 +75,7 @@
// -------------------------------------------------------------------------------------------------
-typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes;
+typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1, IPowerV1_2, IPowerV1_3> PowerHalTypes;
TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes);
TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) {
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
index b54762c..0cd2e22 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp
@@ -87,18 +87,28 @@
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) {
{
InSequence seq;
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(false)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(false)))
+ .Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
EXPECT_CALL(*mMockHal.get(),
setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
@@ -131,6 +141,16 @@
}
TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
index d30e8d2..32f84e2 100644
--- a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp
@@ -31,7 +31,6 @@
using android::hardware::power::V1_0::Feature;
using android::hardware::power::V1_0::PowerHint;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
-using IPowerV1_0 = android::hardware::power::V1_0::IPower;
using namespace android;
using namespace android::power;
@@ -40,15 +39,6 @@
// -------------------------------------------------------------------------------------------------
-class MockIPowerV1_0 : public IPowerV1_0 {
-public:
- MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
- MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
- MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
- MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
- (getPlatformLowPowerStats_cb _hidl_cb), (override));
-};
-
class MockIPowerV1_1 : public IPowerV1_1 {
public:
MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
@@ -69,23 +59,22 @@
protected:
std::unique_ptr<HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr;
- sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr;
+ sp<StrictMock<MockIPowerV1_1>> mMockHal = nullptr;
};
// -------------------------------------------------------------------------------------------------
void PowerHalWrapperHidlV1_1Test::SetUp() {
- mMockHalV1_0 = new StrictMock<MockIPowerV1_0>();
- mMockHalV1_1 = new StrictMock<MockIPowerV1_1>();
- mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1);
+ mMockHal = new StrictMock<MockIPowerV1_1>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHal);
ASSERT_NE(mWrapper, nullptr);
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
}
// -------------------------------------------------------------------------------------------------
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) {
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
.Times(Exactly(1));
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
@@ -93,7 +82,7 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) {
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000)))
.Times(Exactly(1))
.WillRepeatedly([](PowerHint, int32_t) {
return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
@@ -104,24 +93,31 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) {
{
InSequence seq;
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(true)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(false)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_1.get(),
- powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(true)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(false)))
.Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1));
- EXPECT_CALL(*mMockHalV1_0.get(),
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
.Times(Exactly(1));
}
@@ -141,7 +137,7 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) {
- EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1)))
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(true)))
.Times(Exactly(1))
.WillRepeatedly([](PowerHint, int32_t) {
return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
@@ -152,6 +148,16 @@
}
TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
new file mode 100644
index 0000000..cf48409
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_2Test.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 LOG_TAG "PowerHalWrapperHidlV1_2Test"
+
+#include <android/hardware/power/1.2/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
+using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
+
+using IPowerV1_2 = android::hardware::power::V1_2::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_2 : public IPowerV1_2 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHintV1_0 hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHintV1_0 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_2, (PowerHintV1_2 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_2Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_2>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_2Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_2>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_2>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_SHOT), Eq(500)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_LAUNCH), Eq(300)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 500);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 300);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_2, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::AUDIO_LAUNCH, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LAUNCH), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LOW_POWER), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::SUSTAINED_PERFORMANCE), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::VR_MODE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(true));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_STREAMING), Eq(true)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::CAMERA_STREAMING), Eq(false)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_2(Eq(PowerHintV1_2::AUDIO_LOW_LATENCY), Eq(true)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_SECURE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_LOW, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_MID, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::AUDIO_STREAMING_LOW_LATENCY, true);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(Eq(PowerHintV1_2::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_2, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_2Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
new file mode 100644
index 0000000..2c48537
--- /dev/null
+++ b/services/powermanager/tests/PowerHalWrapperHidlV1_3Test.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 LOG_TAG "PowerHalWrapperHidlV1_3Test"
+
+#include <android/hardware/power/1.3/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+#include <binder/IServiceManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <powermanager/PowerHalWrapper.h>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using PowerHintV1_0 = android::hardware::power::V1_0::PowerHint;
+using PowerHintV1_2 = android::hardware::power::V1_2::PowerHint;
+using PowerHintV1_3 = android::hardware::power::V1_3::PowerHint;
+
+using IPowerV1_3 = android::hardware::power::V1_3::IPower;
+
+using namespace android;
+using namespace android::power;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_3 : public IPowerV1_3 {
+public:
+ MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHintV1_0 hint, int32_t data), (override));
+ MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+ MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats,
+ (getPlatformLowPowerStats_cb _hidl_cb), (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHintV1_0 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_2, (PowerHintV1_2 hint, int32_t data),
+ (override));
+ MOCK_METHOD(hardware::Return<void>, powerHintAsync_1_3, (PowerHintV1_3 hint, int32_t data),
+ (override));
+
+ MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats,
+ (getSubsystemLowPowerStats_cb _hidl_cb), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalWrapperHidlV1_3Test : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::unique_ptr<HalWrapper> mWrapper = nullptr;
+ sp<StrictMock<MockIPowerV1_3>> mMockHal = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalWrapperHidlV1_3Test::SetUp() {
+ mMockHal = new StrictMock<MockIPowerV1_3>();
+ mWrapper = std::make_unique<HidlHalWrapperV1_3>(mMockHal);
+ ASSERT_NE(mWrapper, nullptr);
+ EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_2(_, _)).Times(0);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostSuccessful) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::INTERACTION), Eq(1000)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_SHOT), Eq(500)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_LAUNCH), Eq(300)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_SHOT, 500);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 300);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::INTERACTION), Eq(1000)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_3, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetBoostUnsupported) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setBoost(Boost::ML_ACC, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 10);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setBoost(Boost::AUDIO_LAUNCH, 10);
+ ASSERT_TRUE(result.isUnsupported());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetMode) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LAUNCH), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LOW_POWER), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::SUSTAINED_PERFORMANCE), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::VR_MODE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(true));
+ EXPECT_CALL(*mMockHal.get(),
+ setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_STREAMING), Eq(true)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::CAMERA_STREAMING), Eq(false)))
+ .Times(Exactly(2));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::AUDIO_LOW_LATENCY), Eq(true)))
+ .Times(Exactly(1));
+ EXPECT_CALL(*mMockHal.get(),
+ powerHintAsync_1_3(Eq(PowerHintV1_3::EXPENSIVE_RENDERING), Eq(false)))
+ .Times(Exactly(1));
+ }
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::LOW_POWER, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::VR, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::INTERACTIVE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_SECURE, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_LOW, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_MID, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, false);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::AUDIO_STREAMING_LOW_LATENCY, true);
+ ASSERT_TRUE(result.isOk());
+ result = mWrapper->setMode(Mode::EXPENSIVE_RENDERING, false);
+ ASSERT_TRUE(result.isOk());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetModeFailed) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(Eq(PowerHintV1_3::LAUNCH), Eq(1)))
+ .Times(Exactly(1))
+ .WillRepeatedly([](PowerHintV1_3, int32_t) {
+ return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+ });
+
+ auto result = mWrapper->setMode(Mode::LAUNCH, 1);
+ ASSERT_TRUE(result.isFailed());
+}
+
+TEST_F(PowerHalWrapperHidlV1_3Test, TestSetModeIgnored) {
+ EXPECT_CALL(*mMockHal.get(), powerHintAsync_1_3(_, _)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setInteractive(_)).Times(0);
+ EXPECT_CALL(*mMockHal.get(), setFeature(_, _)).Times(0);
+
+ auto result = mWrapper->setMode(Mode::GAME, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::FIXED_PERFORMANCE, true);
+ ASSERT_TRUE(result.isUnsupported());
+ result = mWrapper->setMode(Mode::GAME_LOADING, false);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index f67c610..f5b360f 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -23,6 +23,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <aidl/sensors/convert.h>
using ::aidl::android::hardware::sensors::AdditionalInfo;
using ::aidl::android::hardware::sensors::DynamicSensorInfo;
@@ -34,469 +35,16 @@
using ::android::AidlMessageQueue;
using ::android::hardware::EventFlag;
using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+using ::android::hardware::sensors::implementation::convertToStatus;
+using ::android::hardware::sensors::implementation::convertToSensor;
+using ::android::hardware::sensors::implementation::convertToSensorEvent;
+using ::android::hardware::sensors::implementation::convertFromSensorEvent;
+
namespace android {
namespace {
-status_t convertToStatus(ndk::ScopedAStatus status) {
- if (status.isOk()) {
- return OK;
- } else {
- switch (status.getExceptionCode()) {
- case EX_ILLEGAL_ARGUMENT: {
- return BAD_VALUE;
- }
- case EX_SECURITY: {
- return PERMISSION_DENIED;
- }
- case EX_UNSUPPORTED_OPERATION: {
- return INVALID_OPERATION;
- }
- case EX_SERVICE_SPECIFIC: {
- switch (status.getServiceSpecificError()) {
- case ISensors::ERROR_BAD_VALUE: {
- return BAD_VALUE;
- }
- case ISensors::ERROR_NO_MEMORY: {
- return NO_MEMORY;
- }
- default: {
- return UNKNOWN_ERROR;
- }
- }
- }
- default: {
- return UNKNOWN_ERROR;
- }
- }
- }
-}
-
-void convertToSensor(const SensorInfo &src, sensor_t *dst) {
- dst->name = strdup(src.name.c_str());
- dst->vendor = strdup(src.vendor.c_str());
- dst->version = src.version;
- dst->handle = src.sensorHandle;
- dst->type = (int)src.type;
- dst->maxRange = src.maxRange;
- dst->resolution = src.resolution;
- dst->power = src.power;
- dst->minDelay = src.minDelayUs;
- dst->fifoReservedEventCount = src.fifoReservedEventCount;
- dst->fifoMaxEventCount = src.fifoMaxEventCount;
- dst->stringType = strdup(src.typeAsString.c_str());
- dst->requiredPermission = strdup(src.requiredPermission.c_str());
- dst->maxDelay = src.maxDelayUs;
- dst->flags = src.flags;
- dst->reserved[0] = dst->reserved[1] = 0;
-}
-
-void convertToSensorEvent(const Event &src, sensors_event_t *dst) {
- *dst = {.version = sizeof(sensors_event_t),
- .sensor = src.sensorHandle,
- .type = (int32_t)src.sensorType,
- .reserved0 = 0,
- .timestamp = src.timestamp};
-
- switch (src.sensorType) {
- case SensorType::META_DATA: {
- // Legacy HALs expect the handle reference in the meta data field.
- // Copy it over from the handle of the event.
- dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what;
- dst->meta_data.sensor = src.sensorHandle;
- // Set the sensor handle to 0 to maintain compatibility.
- dst->sensor = 0;
- break;
- }
-
- case SensorType::ACCELEROMETER:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::GYROSCOPE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION: {
- dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x;
- dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y;
- dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z;
- dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status;
- break;
- }
-
- case SensorType::GAME_ROTATION_VECTOR: {
- dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x;
- dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y;
- dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z;
- dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w;
- break;
- }
-
- case SensorType::ROTATION_VECTOR:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
- dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0];
- dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1];
- dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2];
- dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3];
- dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4];
- break;
- }
-
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::ACCELEROMETER_UNCALIBRATED: {
- dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x;
- dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y;
- dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z;
- dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias;
- dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias;
- dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias;
- break;
- }
-
- case SensorType::HINGE_ANGLE:
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::LIGHT:
- case SensorType::PRESSURE:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::STATIONARY_DETECT:
- case SensorType::MOTION_DETECT:
- case SensorType::HEART_BEAT:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT: {
- dst->data[0] = src.payload.get<Event::EventPayload::scalar>();
- break;
- }
-
- case SensorType::STEP_COUNTER: {
- dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>();
- break;
- }
-
- case SensorType::HEART_RATE: {
- dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm;
- dst->heart_rate.status =
- (int8_t)src.payload.get<Event::EventPayload::heartRate>().status;
- break;
- }
-
- case SensorType::POSE_6DOF: { // 15 floats
- for (size_t i = 0; i < 15; ++i) {
- dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i];
- }
- break;
- }
-
- case SensorType::DYNAMIC_SENSOR_META: {
- dst->dynamic_sensor_meta.connected =
- src.payload.get<Event::EventPayload::dynamic>().connected;
- dst->dynamic_sensor_meta.handle =
- src.payload.get<Event::EventPayload::dynamic>().sensorHandle;
- dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later
-
- memcpy(dst->dynamic_sensor_meta.uuid,
- src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16);
-
- break;
- }
-
- case SensorType::ADDITIONAL_INFO: {
- const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>();
-
- additional_info_event_t *dstInfo = &dst->additional_info;
- dstInfo->type = (int32_t)srcInfo.type;
- dstInfo->serial = srcInfo.serial;
-
- switch (srcInfo.payload.getTag()) {
- case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
- const auto &values =
- srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
- .values;
- CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32));
- memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32));
- break;
- }
- case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
- const auto &values =
- srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
- .values;
- CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float));
- memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float));
- break;
- }
- default: {
- ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag());
- }
- }
- break;
- }
-
- case SensorType::HEAD_TRACKER: {
- const auto &ht = src.payload.get<Event::EventPayload::headTracker>();
- dst->head_tracker.rx = ht.rx;
- dst->head_tracker.ry = ht.ry;
- dst->head_tracker.rz = ht.rz;
- dst->head_tracker.vx = ht.vx;
- dst->head_tracker.vy = ht.vy;
- dst->head_tracker.vz = ht.vz;
- dst->head_tracker.discontinuity_count = ht.discontinuityCount;
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES:
- case SensorType::GYROSCOPE_LIMITED_AXES:
- dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x;
- dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y;
- dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z;
- dst->limited_axes_imu.x_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported;
- dst->limited_axes_imu.y_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported;
- dst->limited_axes_imu.z_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported;
- break;
-
- case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
- case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED:
- dst->limited_axes_imu_uncalibrated.x_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x;
- dst->limited_axes_imu_uncalibrated.y_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y;
- dst->limited_axes_imu_uncalibrated.z_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z;
- dst->limited_axes_imu_uncalibrated.x_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias;
- dst->limited_axes_imu_uncalibrated.y_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias;
- dst->limited_axes_imu_uncalibrated.z_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias;
- dst->limited_axes_imu_uncalibrated.x_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported;
- dst->limited_axes_imu_uncalibrated.y_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported;
- dst->limited_axes_imu_uncalibrated.z_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported;
- break;
-
- case SensorType::HEADING:
- dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading;
- dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy;
- break;
-
- default: {
- CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
- memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(),
- 16 * sizeof(float));
- break;
- }
- }
-}
-
-void convertFromSensorEvent(const sensors_event_t &src, Event *dst) {
- *dst = {
- .timestamp = src.timestamp,
- .sensorHandle = src.sensor,
- .sensorType = (SensorType)src.type,
- };
-
- switch (dst->sensorType) {
- case SensorType::META_DATA: {
- Event::EventPayload::MetaData meta;
- meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what;
- // Legacy HALs contain the handle reference in the meta data field.
- // Copy that over to the handle of the event. In legacy HALs this
- // field was expected to be 0.
- dst->sensorHandle = src.meta_data.sensor;
- dst->payload.set<Event::EventPayload::Tag::meta>(meta);
- break;
- }
-
- case SensorType::ACCELEROMETER:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::GYROSCOPE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION: {
- Event::EventPayload::Vec3 vec3;
- vec3.x = src.acceleration.x;
- vec3.y = src.acceleration.y;
- vec3.z = src.acceleration.z;
- vec3.status = (SensorStatus)src.acceleration.status;
- dst->payload.set<Event::EventPayload::Tag::vec3>(vec3);
- break;
- }
-
- case SensorType::GAME_ROTATION_VECTOR: {
- Event::EventPayload::Vec4 vec4;
- vec4.x = src.data[0];
- vec4.y = src.data[1];
- vec4.z = src.data[2];
- vec4.w = src.data[3];
- dst->payload.set<Event::EventPayload::Tag::vec4>(vec4);
- break;
- }
-
- case SensorType::ROTATION_VECTOR:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
- Event::EventPayload::Data data;
- memcpy(data.values.data(), src.data, 5 * sizeof(float));
- dst->payload.set<Event::EventPayload::Tag::data>(data);
- break;
- }
-
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::ACCELEROMETER_UNCALIBRATED: {
- Event::EventPayload::Uncal uncal;
- uncal.x = src.uncalibrated_gyro.x_uncalib;
- uncal.y = src.uncalibrated_gyro.y_uncalib;
- uncal.z = src.uncalibrated_gyro.z_uncalib;
- uncal.xBias = src.uncalibrated_gyro.x_bias;
- uncal.yBias = src.uncalibrated_gyro.y_bias;
- uncal.zBias = src.uncalibrated_gyro.z_bias;
- dst->payload.set<Event::EventPayload::Tag::uncal>(uncal);
- break;
- }
-
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::LIGHT:
- case SensorType::PRESSURE:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::STATIONARY_DETECT:
- case SensorType::MOTION_DETECT:
- case SensorType::HEART_BEAT:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT:
- case SensorType::HINGE_ANGLE: {
- dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]);
- break;
- }
-
- case SensorType::STEP_COUNTER: {
- dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter);
- break;
- }
-
- case SensorType::HEART_RATE: {
- Event::EventPayload::HeartRate heartRate;
- heartRate.bpm = src.heart_rate.bpm;
- heartRate.status = (SensorStatus)src.heart_rate.status;
- dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate);
- break;
- }
-
- case SensorType::POSE_6DOF: { // 15 floats
- Event::EventPayload::Pose6Dof pose6DOF;
- for (size_t i = 0; i < 15; ++i) {
- pose6DOF.values[i] = src.data[i];
- }
- dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF);
- break;
- }
-
- case SensorType::DYNAMIC_SENSOR_META: {
- DynamicSensorInfo dynamic;
- dynamic.connected = src.dynamic_sensor_meta.connected;
- dynamic.sensorHandle = src.dynamic_sensor_meta.handle;
-
- memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16);
- dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic);
- break;
- }
-
- case SensorType::ADDITIONAL_INFO: {
- AdditionalInfo info;
- const additional_info_event_t &srcInfo = src.additional_info;
- info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type;
- info.serial = srcInfo.serial;
-
- AdditionalInfo::AdditionalInfoPayload::Int32Values data;
- CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32));
- memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32));
- info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data);
-
- dst->payload.set<Event::EventPayload::Tag::additional>(info);
- break;
- }
-
- case SensorType::HEAD_TRACKER: {
- Event::EventPayload::HeadTracker headTracker;
- headTracker.rx = src.head_tracker.rx;
- headTracker.ry = src.head_tracker.ry;
- headTracker.rz = src.head_tracker.rz;
- headTracker.vx = src.head_tracker.vx;
- headTracker.vy = src.head_tracker.vy;
- headTracker.vz = src.head_tracker.vz;
- headTracker.discontinuityCount = src.head_tracker.discontinuity_count;
-
- dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES:
- case SensorType::GYROSCOPE_LIMITED_AXES: {
- Event::EventPayload::LimitedAxesImu limitedAxesImu;
- limitedAxesImu.x = src.limited_axes_imu.x;
- limitedAxesImu.y = src.limited_axes_imu.y;
- limitedAxesImu.z = src.limited_axes_imu.z;
- limitedAxesImu.xSupported = src.limited_axes_imu.x_supported;
- limitedAxesImu.ySupported = src.limited_axes_imu.y_supported;
- limitedAxesImu.zSupported = src.limited_axes_imu.z_supported;
- dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu);
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
- case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: {
- Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal;
- limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib;
- limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib;
- limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib;
- limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias;
- limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias;
- limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias;
- limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported;
- limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported;
- limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported;
- dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal);
- break;
- }
-
- case SensorType::HEADING: {
- Event::EventPayload::Heading heading;
- heading.heading = src.heading.heading;
- heading.accuracy = src.heading.accuracy;
- dst->payload.set<Event::EventPayload::heading>(heading);
- break;
- }
-
- default: {
- CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
- Event::EventPayload::Data data;
- memcpy(data.values.data(), src.data, 16 * sizeof(float));
- dst->payload.set<Event::EventPayload::Tag::data>(data);
- break;
- }
- }
-}
-
void serviceDied(void *cookie) {
ALOGW("Sensors HAL died, attempting to reconnect.");
((AidlSensorHalWrapper *)cookie)->prepareForReconnect();
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 5ad4815..0eeb820 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -75,6 +75,7 @@
static_libs: [
"libaidlcommonsupport",
"android.hardware.sensors@1.0-convert",
+ "android.hardware.sensors-V1-convert",
"android.hardware.sensors-V1-ndk",
],
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 46f00e8..398cdf9 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -87,6 +87,42 @@
// ---------------------------------------------------------------------------
+RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback)
+ : BaseSensor(sensor), mCallback(std::move(callback)) {
+}
+
+status_t RuntimeSensor::activate(void*, bool enabled) {
+ if (enabled != mEnabled) {
+ mEnabled = enabled;
+ mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+ }
+ return OK;
+}
+
+status_t RuntimeSensor::batch(void*, int, int, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ if (mSamplingPeriodNs != samplingPeriodNs || mBatchReportLatencyNs != maxBatchReportLatencyNs) {
+ mSamplingPeriodNs = samplingPeriodNs;
+ mBatchReportLatencyNs = maxBatchReportLatencyNs;
+ if (mEnabled) {
+ mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+ }
+ }
+ return OK;
+}
+
+status_t RuntimeSensor::setDelay(void*, int, int64_t ns) {
+ if (mSamplingPeriodNs != ns) {
+ mSamplingPeriodNs = ns;
+ if (mEnabled) {
+ mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+ }
+ }
+ return OK;
+}
+
+// ---------------------------------------------------------------------------
+
ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service)
: HardwareSensor(sensor), mSensorService(service) {
}
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index 5704359..5ee5e12 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -104,6 +104,32 @@
// ---------------------------------------------------------------------------
+class RuntimeSensor : public BaseSensor {
+public:
+ static constexpr int DEFAULT_DEVICE_ID = 0;
+
+ class StateChangeCallback : public virtual RefBase {
+ public:
+ virtual void onStateChanged(bool enabled, int64_t samplingPeriodNs,
+ int64_t batchReportLatencyNs) = 0;
+ };
+ RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback);
+ virtual status_t activate(void* ident, bool enabled) override;
+ virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) override;
+ virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
+ virtual bool process(sensors_event_t*, const sensors_event_t&) { return false; }
+ virtual bool isVirtual() const override { return false; }
+
+private:
+ bool mEnabled = false;
+ int64_t mSamplingPeriodNs = 0;
+ int64_t mBatchReportLatencyNs = 0;
+ sp<StateChangeCallback> mCallback;
+};
+
+// ---------------------------------------------------------------------------
+
class ProximitySensor : public HardwareSensor {
public:
explicit ProximitySensor(const sensor_t& sensor, SensorService& service);
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 85ce0f0..6d36b47 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -29,12 +29,12 @@
const Sensor SensorList::mNonSensor = Sensor("unknown");
bool SensorList::add(
- int handle, SensorInterface* si, bool isForDebug, bool isVirtual) {
+ int handle, SensorInterface* si, bool isForDebug, bool isVirtual, int deviceId) {
std::lock_guard<std::mutex> lk(mLock);
if (handle == si->getSensor().getHandle() &&
mUsedHandle.insert(handle).second) {
// will succeed as the mUsedHandle does not have this handle
- mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual));
+ mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual, deviceId));
return true;
}
// handle exist already or handle mismatch
@@ -79,7 +79,8 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) {
+ if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()
+ && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
sensors.add(e.si->getSensor());
}
return true;
@@ -92,7 +93,8 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (!e.si->getSensor().isDynamicSensor()) {
+ if (!e.si->getSensor().isDynamicSensor()
+ && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
sensors.add(e.si->getSensor());
}
return true;
@@ -105,7 +107,8 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) {
+ if (!e.isForDebug && e.si->getSensor().isDynamicSensor()
+ && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
sensors.add(e.si->getSensor());
}
return true;
@@ -118,7 +121,20 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (e.isVirtual) {
+ if (e.isVirtual && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ sensors.add(e.si->getSensor());
+ }
+ return true;
+ });
+ return sensors;
+}
+
+const Vector<Sensor> SensorList::getRuntimeSensors(int deviceId) const {
+ // lock in forEachEntry
+ Vector<Sensor> sensors;
+ forEachEntry(
+ [&sensors, deviceId] (const Entry& e) -> bool {
+ if (!e.isForDebug && e.deviceId == deviceId) {
sensors.add(e.si->getSensor());
}
return true;
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 049ae7c..79f6701 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -40,14 +40,16 @@
sp<SensorInterface> si;
const bool isForDebug;
const bool isVirtual;
- Entry(SensorInterface* si_, bool debug_, bool virtual_) :
- si(si_), isForDebug(debug_), isVirtual(virtual_) {
+ const int deviceId;
+ Entry(SensorInterface* si_, bool debug_, bool virtual_, int deviceId_) :
+ si(si_), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
}
};
// After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
// object it pointed to and the object should not be released elsewhere.
- bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false);
+ bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false,
+ int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
// After a handle is removed, the object that SensorInterface * pointing to may get deleted if
// no more sp<> of the same object exist.
@@ -60,6 +62,7 @@
const Vector<Sensor> getUserDebugSensors() const;
const Vector<Sensor> getDynamicSensors() const;
const Vector<Sensor> getVirtualSensors() const;
+ const Vector<Sensor> getRuntimeSensors(int deviceId) const;
String8 getName(int handle) const;
String8 getStringType(int handle) const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 21d6b6b..0c9fef5 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -102,6 +102,33 @@
static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE");
static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS");
+namespace {
+
+// TODO(b/259227294): Move the sensor ranges to the HAL.
+int32_t nextRuntimeSensorHandle() {
+ static constexpr int32_t kRuntimeHandleBase = 0x5F000000;
+ static constexpr int32_t kRuntimeHandleEnd = 0x5FFFFFFF;
+ static int32_t nextHandle = kRuntimeHandleBase;
+ if (nextHandle == kRuntimeHandleEnd) {
+ return -1;
+ }
+ return nextHandle++;
+}
+
+class RuntimeSensorCallbackProxy : public RuntimeSensor::StateChangeCallback {
+ public:
+ RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorStateChangeCallback> callback)
+ : mCallback(std::move(callback)) {}
+ void onStateChanged(bool enabled, int64_t samplingPeriodNs,
+ int64_t batchReportLatencyNs) override {
+ mCallback->onStateChanged(enabled, samplingPeriodNs, batchReportLatencyNs);
+ }
+ private:
+ sp<SensorService::RuntimeSensorStateChangeCallback> mCallback;
+};
+
+} // namespace
+
static bool isAutomotive() {
sp<IServiceManager> serviceManager = defaultServiceManager();
if (serviceManager.get() == nullptr) {
@@ -137,6 +164,60 @@
mMicSensorPrivacyPolicy = new MicrophonePrivacyPolicy(this);
}
+int SensorService::registerRuntimeSensor(
+ const sensor_t& sensor, int deviceId, sp<RuntimeSensorStateChangeCallback> callback) {
+ int handle = 0;
+ while (handle == 0 || !mSensors.isNewHandle(handle)) {
+ handle = nextRuntimeSensorHandle();
+ if (handle < 0) {
+ // Ran out of the dedicated range for runtime sensors.
+ return handle;
+ }
+ }
+
+ ALOGI("Registering runtime sensor handle 0x%x, type %d, name %s",
+ handle, sensor.type, sensor.name);
+
+ sp<RuntimeSensor::StateChangeCallback> runtimeSensorCallback(
+ new RuntimeSensorCallbackProxy(std::move(callback)));
+ sensor_t runtimeSensor = sensor;
+ // force the handle to be consistent
+ runtimeSensor.handle = handle;
+ SensorInterface *si = new RuntimeSensor(runtimeSensor, std::move(runtimeSensorCallback));
+
+ Mutex::Autolock _l(mLock);
+ const Sensor& s = registerSensor(si, /* isDebug= */ false, /* isVirtual= */ false, deviceId);
+
+ if (s.getHandle() != handle) {
+ // The registration was unsuccessful.
+ return s.getHandle();
+ }
+ return handle;
+}
+
+status_t SensorService::unregisterRuntimeSensor(int handle) {
+ ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle);
+ {
+ Mutex::Autolock _l(mLock);
+ if (!unregisterDynamicSensorLocked(handle)) {
+ ALOGE("Runtime sensor release error.");
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
+ connection->removeSensor(handle);
+ }
+ return OK;
+}
+
+status_t SensorService::sendRuntimeSensorEvent(const sensors_event_t& event) {
+ Mutex::Autolock _l(mLock);
+ mRuntimeSensorEventQueue.push(event);
+ return OK;
+}
+
bool SensorService::initializeHmacKey() {
int fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_RDONLY|O_CLOEXEC);
if (fd != -1) {
@@ -407,10 +488,11 @@
&& isUidActive(uid) && !isOperationRestrictedLocked(opPackageName);
}
-const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) {
+const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual,
+ int deviceId) {
int handle = s->getSensor().getHandle();
int type = s->getSensor().getType();
- if (mSensors.add(handle, s, isDebug, isVirtual)){
+ if (mSensors.add(handle, s, isDebug, isVirtual, deviceId)) {
mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
return s->getSensor();
} else {
@@ -1003,6 +1085,7 @@
recordLastValueLocked(mSensorEventBuffer, count);
// handle virtual sensors
+ bool bufferNeedsSorting = false;
if (count && vcount) {
sensors_event_t const * const event = mSensorEventBuffer;
if (!mActiveVirtualSensors.empty()) {
@@ -1038,12 +1121,37 @@
// record the last synthesized values
recordLastValueLocked(&mSensorEventBuffer[count], k);
count += k;
- // sort the buffer by time-stamps
- sortEventBuffer(mSensorEventBuffer, count);
+ bufferNeedsSorting = true;
}
}
}
+ // handle runtime sensors
+ {
+ size_t k = 0;
+ while (!mRuntimeSensorEventQueue.empty()) {
+ if (count + k >= minBufferSize) {
+ ALOGE("buffer too small to hold all events: count=%zd, k=%zu, size=%zu",
+ count, k, minBufferSize);
+ break;
+ }
+ mSensorEventBuffer[count + k] = mRuntimeSensorEventQueue.front();
+ mRuntimeSensorEventQueue.pop();
+ k++;
+ }
+ if (k) {
+ // record the last synthesized values
+ recordLastValueLocked(&mSensorEventBuffer[count], k);
+ count += k;
+ bufferNeedsSorting = true;
+ }
+ }
+
+ if (bufferNeedsSorting) {
+ // sort the buffer by time-stamps
+ sortEventBuffer(mSensorEventBuffer, count);
+ }
+
// handle backward compatibility for RotationVector sensor
if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) {
for (int i = 0; i < count; i++) {
@@ -1342,19 +1450,37 @@
return accessibleSensorList;
}
+void SensorService::addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor,
+ Vector<Sensor>& accessibleSensorList) {
+ if (canAccessSensor(sensor, "can't see", opPackageName)) {
+ accessibleSensorList.add(sensor);
+ } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
+ ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
+ sensor.getName().string(), sensor.getRequiredPermission().string(),
+ sensor.getRequiredAppOp());
+ }
+}
+
Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) {
Vector<Sensor> accessibleSensorList;
mSensors.forEachSensor(
[this, &opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool {
if (sensor.isDynamicSensor()) {
- if (canAccessSensor(sensor, "can't see", opPackageName)) {
- accessibleSensorList.add(sensor);
- } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
- ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
- sensor.getName().string(),
- sensor.getRequiredPermission().string(),
- sensor.getRequiredAppOp());
- }
+ addSensorIfAccessible(opPackageName, sensor, accessibleSensorList);
+ }
+ return true;
+ });
+ makeUuidsIntoIdsForSensorList(accessibleSensorList);
+ return accessibleSensorList;
+}
+
+Vector<Sensor> SensorService::getRuntimeSensorList(const String16& opPackageName, int deviceId) {
+ Vector<Sensor> accessibleSensorList;
+ mSensors.forEachEntry(
+ [this, &opPackageName, deviceId, &accessibleSensorList] (
+ const SensorServiceUtil::SensorList::Entry& e) -> bool {
+ if (e.deviceId == deviceId) {
+ addSensorIfAccessible(opPackageName, e.si->getSensor(), accessibleSensorList);
}
return true;
});
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 4ba3c51..e490398 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -42,6 +42,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -143,6 +144,14 @@
virtual void onProximityActive(bool isActive) = 0;
};
+ class RuntimeSensorStateChangeCallback : public virtual RefBase {
+ public:
+ // Note that the callback is invoked from an async thread and can interact with the
+ // SensorService directly.
+ virtual void onStateChanged(bool enabled, int64_t samplingPeriodNanos,
+ int64_t batchReportLatencyNanos) = 0;
+ };
+
static char const* getServiceName() ANDROID_API { return "sensorservice"; }
SensorService() ANDROID_API;
@@ -169,6 +178,11 @@
status_t addProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
+ int registerRuntimeSensor(const sensor_t& sensor, int deviceId,
+ sp<RuntimeSensorStateChangeCallback> callback) ANDROID_API;
+ status_t unregisterRuntimeSensor(int handle) ANDROID_API;
+ status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
+
// Returns true if a sensor should be throttled according to our rate-throttling rules.
static bool isSensorInCappedSet(int sensorType);
@@ -346,6 +360,7 @@
// ISensorServer interface
virtual Vector<Sensor> getSensorList(const String16& opPackageName);
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName);
+ virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId);
virtual sp<ISensorEventConnection> createSensorEventConnection(
const String8& packageName,
int requestedMode, const String16& opPackageName, const String16& attributionTag);
@@ -364,8 +379,9 @@
bool isWakeUpSensor(int type) const;
void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
- const Sensor& registerSensor(SensorInterface* sensor,
- bool isDebug = false, bool isVirtual = false);
+ const Sensor& registerSensor(SensorInterface* sensor, bool isDebug = false,
+ bool isVirtual = false,
+ int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false);
const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false);
bool unregisterDynamicSensorLocked(int handle);
@@ -375,6 +391,8 @@
sensors_event_t const* buffer, const int count);
bool canAccessSensor(const Sensor& sensor, const char* operation,
const String16& opPackageName);
+ void addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor,
+ Vector<Sensor>& accessibleSensorList);
static bool hasPermissionForSensor(const Sensor& sensor);
static int getTargetSdkVersion(const String16& opPackageName);
static void resetTargetSdkVersionCache(const String16& opPackageName);
@@ -492,6 +510,7 @@
wp<const SensorEventConnection> * mMapFlushEventsToConnections;
std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
Mode mCurrentOperatingMode;
+ std::queue<sensors_event_t> mRuntimeSensorEventQueue;
// true if the head tracker sensor type is currently restricted to system usage only
// (can only be unrestricted for testing, via shell cmd)
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
new file mode 100644
index 0000000..34d1de7
--- /dev/null
+++ b/services/sensorservice/aidl/Android.bp
@@ -0,0 +1,44 @@
+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 {
+ name: "libsensorserviceaidl",
+ srcs: [
+ "EventQueue.cpp",
+ "DirectReportChannel.cpp",
+ "SensorManager.cpp",
+ "utils.cpp",
+ ],
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ header_libs: ["jni_headers"],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ "libcutils",
+ "libbinder_ndk",
+ "libsensor",
+ "android.frameworks.sensorservice-V1-ndk",
+ "android.hardware.sensors-V1-ndk",
+ ],
+ export_include_dirs: [
+ "include/",
+ ],
+ static_libs: [
+ "android.hardware.sensors-V1-convert",
+ ],
+
+ export_header_lib_headers: ["jni_headers"],
+ local_include_dirs: [
+ "include/sensorserviceaidl/",
+ ],
+}
diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp
new file mode 100644
index 0000000..cab53c1
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "DirectReportChannel.h"
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId)
+ : mManager(manager), mId(channelId) {}
+
+DirectReportChannel::~DirectReportChannel() {
+ mManager.destroyDirectChannel(mId);
+}
+
+ndk::ScopedAStatus DirectReportChannel::configure(
+ int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+ int32_t* _aidl_return) {
+ int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate));
+ if (token <= 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(token);
+ }
+ *_aidl_return = token;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/DirectReportChannel.h b/services/sensorservice/aidl/DirectReportChannel.h
new file mode 100644
index 0000000..d9ea73d
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.h
@@ -0,0 +1,45 @@
+/*
+ * 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
+
+#include <aidl/android/frameworks/sensorservice/BnDirectReportChannel.h>
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class DirectReportChannel final
+ : public ::aidl::android::frameworks::sensorservice::BnDirectReportChannel {
+public:
+ DirectReportChannel(::android::SensorManager& manager, int channelId);
+ ~DirectReportChannel();
+
+ ndk::ScopedAStatus configure(int32_t sensorHandle,
+ ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+ int32_t* _aidl_return) override;
+
+private:
+ ::android::SensorManager& mManager;
+ const int mId;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp
new file mode 100644
index 0000000..c394709
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 "EventQueue.h"
+#include "utils.h"
+
+#include <android-base/logging.h>
+#include <utils/Looper.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::hardware::sensors::Event;
+
+class EventQueueLooperCallback : public ::android::LooperCallback {
+public:
+ EventQueueLooperCallback(sp<::android::SensorEventQueue> queue,
+ std::shared_ptr<IEventQueueCallback> callback)
+ : mQueue(queue), mCallback(callback) {}
+
+ int handleEvent(int /* fd */, int /* events */, void* /* data */) {
+ ASensorEvent event;
+ ssize_t actual;
+
+ auto internalQueue = mQueue.promote();
+ if (internalQueue == nullptr) {
+ return 1;
+ }
+
+ while ((actual = internalQueue->read(&event, 1)) > 0) {
+ internalQueue->sendAck(&event, actual);
+ ndk::ScopedAStatus ret = mCallback->onEvent(convertEvent(event));
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Failed to envoke EventQueueCallback: " << ret;
+ }
+ }
+
+ return 1; // continue to receive callbacks
+ }
+
+private:
+ wp<::android::SensorEventQueue> mQueue;
+ std::shared_ptr<IEventQueueCallback> mCallback;
+};
+
+EventQueue::EventQueue(std::shared_ptr<IEventQueueCallback> callback, sp<::android::Looper> looper,
+ sp<::android::SensorEventQueue> internalQueue)
+ : mLooper(looper), mInternalQueue(internalQueue) {
+ mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
+ new EventQueueLooperCallback(internalQueue, callback), nullptr);
+}
+
+EventQueue::~EventQueue() {
+ mLooper->removeFd(mInternalQueue->getFd());
+}
+
+ndk::ScopedAStatus EventQueue::enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+ int64_t in_maxBatchReportLatencyUs) {
+ return convertResult(mInternalQueue->enableSensor(in_sensorHandle, in_samplingPeriodUs,
+ in_maxBatchReportLatencyUs, 0));
+}
+
+ndk::ScopedAStatus EventQueue::disableSensor(int32_t in_sensorHandle) {
+ return convertResult(mInternalQueue->disableSensor(in_sensorHandle));
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.h b/services/sensorservice/aidl/EventQueue.h
new file mode 100644
index 0000000..0ae1eba
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.h
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+#include "SensorManagerAidl.h"
+
+#include <aidl/android/frameworks/sensorservice/BnEventQueue.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+struct EventQueue final : public aidl::android::frameworks::sensorservice::BnEventQueue {
+ EventQueue(
+ std::shared_ptr<aidl::android::frameworks::sensorservice::IEventQueueCallback> callback,
+ sp<::android::Looper> looper, sp<::android::SensorEventQueue> internalQueue);
+ ~EventQueue();
+
+ ndk::ScopedAStatus enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+ int64_t in_maxBatchReportLatencyUs) override;
+ ndk::ScopedAStatus disableSensor(int32_t sensorHandle) override;
+
+private:
+ friend class EventQueueLooperCallback;
+ sp<::android::Looper> mLooper;
+ sp<::android::SensorEventQueue> mInternalQueue;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
new file mode 100644
index 0000000..9b03344
--- /dev/null
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+// LOG_TAG defined via build flag.
+#ifndef LOG_TAG
+#define LOG_TAG "AidlSensorManager"
+#endif
+
+#include "DirectReportChannel.h"
+#include "EventQueue.h"
+#include "SensorManagerAidl.h"
+#include "utils.h"
+
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <sched.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IDirectReportChannel;
+using ::aidl::android::frameworks::sensorservice::IEventQueue;
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::frameworks::sensorservice::ISensorManager;
+using ::aidl::android::hardware::common::Ashmem;
+using ::aidl::android::hardware::sensors::ISensors;
+using ::aidl::android::hardware::sensors::SensorInfo;
+using ::aidl::android::hardware::sensors::SensorType;
+using ::android::frameworks::sensorservice::implementation::SensorManagerAidl;
+
+static const char* POLL_THREAD_NAME = "aidl_ssvc_poll";
+
+SensorManagerAidl::SensorManagerAidl(JavaVM* vm)
+ : mLooper(new Looper(false)), mStopThread(true), mJavaVm(vm) {}
+SensorManagerAidl::~SensorManagerAidl() {
+ // Stops pollAll inside the thread.
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+
+ mStopThread = true;
+ if (mLooper != nullptr) {
+ mLooper->wake();
+ }
+ if (mPollThread.joinable()) {
+ mPollThread.join();
+ }
+}
+
+ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type,
+ const native_handle_t* handle,
+ std::shared_ptr<IDirectReportChannel>* chan) {
+ int channelId = manager.createDirectChannel(size, type, handle);
+ if (channelId < 0) {
+ return convertResult(channelId);
+ }
+ if (channelId == 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+ *chan = ndk::SharedRefBase::make<DirectReportChannel>(manager, channelId);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createAshmemDirectChannel(
+ const Ashmem& in_mem, int64_t in_size,
+ std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+ if (in_size > in_mem.size || in_size < ISensors::DIRECT_REPORT_SENSOR_EVENT_TOTAL_LENGTH) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+ }
+ native_handle_t* handle = native_handle_create(1, 0);
+ handle->data[0] = dup(in_mem.fd.get());
+
+ auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_ASHMEM,
+ handle, _aidl_return);
+ int result = native_handle_close(handle);
+ CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+ result = native_handle_delete(handle);
+ CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+ return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createGrallocDirectChannel(
+ const ndk::ScopedFileDescriptor& in_mem, int64_t in_size,
+ std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+ native_handle_t* handle = native_handle_create(1, 0);
+ handle->data[0] = dup(in_mem.get());
+
+ auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_GRALLOC,
+ handle, _aidl_return);
+ int result = native_handle_close(handle);
+ CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+ result = native_handle_delete(handle);
+ CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+ return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createEventQueue(
+ const std::shared_ptr<IEventQueueCallback>& in_callback,
+ std::shared_ptr<IEventQueue>* _aidl_return) {
+ if (in_callback == nullptr) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+ }
+
+ sp<::android::Looper> looper = getLooper();
+ if (looper == nullptr) {
+ LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue cannot initialize looper";
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+
+ String8 package(String8::format("aidl_client_pid_%d", AIBinder_getCallingPid()));
+ sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue(package);
+ if (internalQueue == nullptr) {
+ LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue returns nullptr.";
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+
+ *_aidl_return = ndk::SharedRefBase::make<EventQueue>(in_callback, looper, internalQueue);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+SensorInfo convertSensor(Sensor src) {
+ SensorInfo dst;
+ dst.sensorHandle = src.getHandle();
+ dst.name = src.getName();
+ dst.vendor = src.getVendor();
+ dst.version = src.getVersion();
+ dst.type = static_cast<SensorType>(src.getType());
+ dst.typeAsString = src.getStringType();
+ // maxRange uses maxValue because ::android::Sensor wraps the
+ // internal sensor_t in this way.
+ dst.maxRange = src.getMaxValue();
+ dst.resolution = src.getResolution();
+ dst.power = src.getPowerUsage();
+ dst.minDelayUs = src.getMinDelay();
+ dst.fifoReservedEventCount = src.getFifoReservedEventCount();
+ dst.fifoMaxEventCount = src.getFifoMaxEventCount();
+ dst.requiredPermission = src.getRequiredPermission();
+ dst.maxDelayUs = src.getMaxDelay();
+ dst.flags = src.getFlags();
+ return dst;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getDefaultSensor(SensorType in_type,
+ SensorInfo* _aidl_return) {
+ ::android::Sensor const* sensor =
+ getInternalManager().getDefaultSensor(static_cast<int>(in_type));
+ if (!sensor) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_NOT_EXIST);
+ }
+ *_aidl_return = convertSensor(*sensor);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getSensorList(std::vector<SensorInfo>* _aidl_return) {
+ Sensor const* const* list;
+ _aidl_return->clear();
+ ssize_t count = getInternalManager().getSensorList(&list);
+ if (count < 0 || list == nullptr) {
+ LOG(ERROR) << "SensorMAanger::getSensorList failed with count: " << count;
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+ _aidl_return->reserve(static_cast<size_t>(count));
+ for (ssize_t i = 0; i < count; ++i) {
+ _aidl_return->push_back(convertSensor(*list[i]));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+::android::SensorManager& SensorManagerAidl::getInternalManager() {
+ std::lock_guard<std::mutex> lock(mInternalManagerMutex);
+ if (mInternalManager == nullptr) {
+ mInternalManager = &::android::SensorManager::getInstanceForPackage(
+ String16(ISensorManager::descriptor));
+ }
+ return *mInternalManager;
+}
+
+/* One global looper for all event queues created from this SensorManager. */
+sp<Looper> SensorManagerAidl::getLooper() {
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+
+ if (!mPollThread.joinable()) {
+ // if thread not initialized, start thread
+ mStopThread = false;
+ std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
+ struct sched_param p = {};
+ p.sched_priority = 10;
+ if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
+ LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
+ }
+
+ // set looper
+ Looper::setForThread(looper);
+
+ // Attach the thread to JavaVM so that pollAll do not crash if the thread
+ // eventually calls into Java.
+ JavaVMAttachArgs args{.version = JNI_VERSION_1_2,
+ .name = POLL_THREAD_NAME,
+ .group = nullptr};
+ JNIEnv* env;
+ if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
+ LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM.";
+ }
+
+ LOG(INFO) << POLL_THREAD_NAME << " started.";
+ for (;;) {
+ int pollResult = looper->pollAll(-1 /* timeout */);
+ if (pollResult == Looper::POLL_WAKE) {
+ if (stopThread == true) {
+ LOG(INFO) << POLL_THREAD_NAME << ": requested to stop";
+ break;
+ } else {
+ LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work";
+ }
+ } else {
+ LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected "
+ << pollResult;
+ break;
+ }
+ }
+
+ if (javaVm->DetachCurrentThread() != JNI_OK) {
+ LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM.";
+ }
+
+ LOG(INFO) << POLL_THREAD_NAME << " is terminated.";
+ }};
+ mPollThread = std::move(pollThread);
+ }
+ return mLooper;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
new file mode 100644
index 0000000..0d6e476
--- /dev/null
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -0,0 +1,52 @@
+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_fuzz {
+ name: "libsensorserviceaidl_fuzzer",
+ defaults: [
+ "service_fuzzer_defaults",
+ ],
+ host_supported: true,
+ static_libs: [
+ "libsensorserviceaidl",
+ "libpermission",
+ "android.frameworks.sensorservice-V1-ndk",
+ "android.hardware.sensors-V1-convert",
+ "android.hardware.sensors-V1-ndk",
+ "android.hardware.common-V2-ndk",
+ "libsensor",
+ "libfakeservicemanager",
+ "libcutils",
+ "liblog",
+ ],
+ srcs: [
+ "fuzzer.cpp",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-sensors@google.com",
+ "devinmoore@google.com",
+ ],
+ },
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ diag: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ },
+ address: true,
+ integer_overflow: true,
+ },
+
+}
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
new file mode 100644
index 0000000..1b63d76
--- /dev/null
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <ServiceManager.h>
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <fuzzbinder/random_binder.h>
+#include <sensorserviceaidl/SensorManagerAidl.h>
+
+using android::fuzzService;
+using android::frameworks::sensorservice::implementation::SensorManagerAidl;
+using ndk::SharedRefBase;
+
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+ std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
+ fakeServiceManager->clear();
+
+ FuzzedDataProvider fdp(data, size);
+ android::sp<android::IBinder> binder = android::getRandomBinder(&fdp);
+ if (binder == nullptr) {
+ // Nothing to do if we get a null binder. It will cause SensorManager to
+ // hang while trying to get sensorservice.
+ return 0;
+ }
+
+ CHECK(android::NO_ERROR == fakeServiceManager->addService(android::String16("sensorservice"),
+ binder));
+
+ std::shared_ptr<SensorManagerAidl> sensorService =
+ ndk::SharedRefBase::make<SensorManagerAidl>(nullptr);
+
+ fuzzService(sensorService->asBinder().get(), std::move(fdp));
+
+ return 0;
+}
diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
new file mode 100644
index 0000000..c77ee88
--- /dev/null
+++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
@@ -0,0 +1,74 @@
+/*
+ * 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
+
+#include <aidl/android/frameworks/sensorservice/BnSensorManager.h>
+#include <jni.h>
+#include <sensor/SensorManager.h>
+#include <utils/Looper.h>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class SensorManagerAidl : public ::aidl::android::frameworks::sensorservice::BnSensorManager {
+public:
+ explicit SensorManagerAidl(JavaVM* vm);
+ ~SensorManagerAidl();
+
+ ::ndk::ScopedAStatus createAshmemDirectChannel(
+ const ::aidl::android::hardware::common::Ashmem& in_mem, int64_t in_size,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+ _aidl_return) override;
+ ::ndk::ScopedAStatus createEventQueue(
+ const std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueueCallback>&
+ in_callback,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueue>* _aidl_return)
+ override;
+ ::ndk::ScopedAStatus createGrallocDirectChannel(
+ const ::ndk::ScopedFileDescriptor& in_buffer, int64_t in_size,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+ _aidl_return) override;
+ ::ndk::ScopedAStatus getDefaultSensor(
+ ::aidl::android::hardware::sensors::SensorType in_type,
+ ::aidl::android::hardware::sensors::SensorInfo* _aidl_return) override;
+ ::ndk::ScopedAStatus getSensorList(
+ std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override;
+
+private:
+ // Block until ::android::SensorManager is initialized.
+ ::android::SensorManager& getInternalManager();
+ sp<Looper> getLooper();
+
+ std::mutex mInternalManagerMutex;
+ ::android::SensorManager* mInternalManager = nullptr; // does not own
+ sp<Looper> mLooper;
+
+ volatile bool mStopThread;
+ std::mutex mThreadMutex; // protects mPollThread
+ std::thread mPollThread;
+
+ JavaVM* mJavaVm;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
new file mode 100644
index 0000000..beb38b9
--- /dev/null
+++ b/services/sensorservice/aidl/utils.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "utils.h"
+
+#include <aidl/android/frameworks/sensorservice/ISensorManager.h>
+#include <aidl/sensors/convert.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+ndk::ScopedAStatus convertResult(status_t src) {
+ using ::aidl::android::frameworks::sensorservice::ISensorManager;
+
+ int err = 0;
+ switch (src) {
+ case OK:
+ return ndk::ScopedAStatus::ok();
+ case NAME_NOT_FOUND:
+ err = ISensorManager::RESULT_NOT_EXIST;
+ break;
+ case NO_MEMORY:
+ err = ISensorManager::RESULT_NO_MEMORY;
+ break;
+ case NO_INIT:
+ err = ISensorManager::RESULT_NO_INIT;
+ break;
+ case PERMISSION_DENIED:
+ err = ISensorManager::RESULT_PERMISSION_DENIED;
+ break;
+ case BAD_VALUE:
+ err = ISensorManager::RESULT_BAD_VALUE;
+ break;
+ case INVALID_OPERATION:
+ err = ISensorManager::RESULT_INVALID_OPERATION;
+ break;
+ default:
+ err = ISensorManager::RESULT_UNKNOWN_ERROR;
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificError(err);
+}
+
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
+ ::aidl::android::hardware::sensors::Event dst;
+ ::android::hardware::sensors::implementation::
+ convertFromASensorEvent(src, &dst);
+ return dst;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.h b/services/sensorservice/aidl/utils.h
new file mode 100644
index 0000000..06ba59e
--- /dev/null
+++ b/services/sensorservice/aidl/utils.h
@@ -0,0 +1,32 @@
+/*
+ * 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
+
+#include <aidl/android/hardware/sensors/Event.h>
+#include <sensor/Sensor.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+::ndk::ScopedAStatus convertResult(status_t status);
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& event);
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
::android::hardware::sensors::V1_0::Event dst;
- ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
- reinterpret_cast<const sensors_event_t&>(src), &dst);
+ ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent(
+ src, &dst);
return dst;
}
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 3de51a4..9e13849 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -69,6 +69,10 @@
case VendorAtomValue::repeatedIntValue: {
const std::optional<std::vector<int>>& repeatedIntValue =
atomValue.get<VendorAtomValue::repeatedIntValue>();
+ if (!repeatedIntValue) {
+ AStatsEvent_writeInt32Array(event, {}, 0);
+ break;
+ }
AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
repeatedIntValue->size());
break;
@@ -76,6 +80,10 @@
case VendorAtomValue::repeatedLongValue: {
const std::optional<std::vector<int64_t>>& repeatedLongValue =
atomValue.get<VendorAtomValue::repeatedLongValue>();
+ if (!repeatedLongValue) {
+ AStatsEvent_writeInt64Array(event, {}, 0);
+ break;
+ }
AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
repeatedLongValue->size());
break;
@@ -83,6 +91,10 @@
case VendorAtomValue::repeatedFloatValue: {
const std::optional<std::vector<float>>& repeatedFloatValue =
atomValue.get<VendorAtomValue::repeatedFloatValue>();
+ if (!repeatedFloatValue) {
+ AStatsEvent_writeFloatArray(event, {}, 0);
+ break;
+ }
AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
repeatedFloatValue->size());
break;
@@ -90,12 +102,18 @@
case VendorAtomValue::repeatedStringValue: {
const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
atomValue.get<VendorAtomValue::repeatedStringValue>();
+ if (!repeatedStringValue) {
+ AStatsEvent_writeStringArray(event, {}, 0);
+ break;
+ }
const std::vector<std::optional<std::string>>& repeatedStringVector =
*repeatedStringValue;
const char* cStringArray[repeatedStringVector.size()];
for (int i = 0; i < repeatedStringVector.size(); ++i) {
- cStringArray[i] = repeatedStringVector[i]->c_str();
+ cStringArray[i] = repeatedStringVector[i].has_value()
+ ? repeatedStringVector[i]->c_str()
+ : "";
}
AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
@@ -104,6 +122,10 @@
case VendorAtomValue::repeatedBoolValue: {
const std::optional<std::vector<bool>>& repeatedBoolValue =
atomValue.get<VendorAtomValue::repeatedBoolValue>();
+ if (!repeatedBoolValue) {
+ AStatsEvent_writeBoolArray(event, {}, 0);
+ break;
+ }
const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
bool boolArray[repeatedBoolValue->size()];
@@ -117,7 +139,10 @@
case VendorAtomValue::byteArrayValue: {
const std::optional<std::vector<uint8_t>>& byteArrayValue =
atomValue.get<VendorAtomValue::byteArrayValue>();
-
+ if (!byteArrayValue) {
+ AStatsEvent_writeByteArray(event, {}, 0);
+ break;
+ }
AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
break;
}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b911ae7..0a192c5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -49,7 +49,7 @@
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V4-cpp",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -84,7 +84,6 @@
"libserviceutils",
"libshaders",
"libtonemap",
- "libtrace_proto",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -156,14 +155,20 @@
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FrontEnd/LayerCreationArgs.cpp",
+ "FrontEnd/LayerHandle.cpp",
+ "FrontEnd/LayerHierarchy.cpp",
+ "FrontEnd/LayerLifecycleManager.cpp",
+ "FrontEnd/RequestedLayerState.cpp",
+ "FrontEnd/TransactionHandler.cpp",
"FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
- "HwcSlotGenerator.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
+ "LayerFE.cpp",
"LayerProtoHelper.cpp",
"LayerRenderArea.cpp",
"LayerVector.cpp",
@@ -178,7 +183,7 @@
"Scheduler/LayerHistory.cpp",
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
- "Scheduler/RefreshRateConfigs.cpp",
+ "Scheduler/RefreshRateSelector.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
@@ -186,10 +191,10 @@
"Scheduler/VsyncConfiguration.cpp",
"Scheduler/VsyncModulator.cpp",
"Scheduler/VsyncSchedule.cpp",
+ "ScreenCaptureOutput.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
- "SurfaceInterceptor.cpp",
"Tracing/LayerTracing.cpp",
"Tracing/TransactionTracing.cpp",
"Tracing/TransactionProtoParser.cpp",
@@ -224,7 +229,6 @@
],
static_libs: [
"libserviceutils",
- "libtrace_proto",
],
}
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 3685bb4..bdbc79b 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -24,6 +24,8 @@
#include <gui/AidlStatusUtil.h>
#include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "SurfaceFlinger.h"
@@ -46,58 +48,21 @@
return NO_ERROR;
}
-void Client::attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer)
-{
- Mutex::Autolock _l(mLock);
- mLayers.add(handle, layer);
-}
-
-void Client::detachLayer(const Layer* layer)
-{
- Mutex::Autolock _l(mLock);
- // we do a linear search here, because this doesn't happen often
- const size_t count = mLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- if (mLayers.valueAt(i) == layer) {
- mLayers.removeItemsAt(i, 1);
- break;
- }
- }
-}
-sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const
-{
- Mutex::Autolock _l(mLock);
- sp<Layer> lbc;
- wp<Layer> layer(mLayers.valueFor(handle));
- if (layer != 0) {
- lbc = layer.promote();
- ALOGE_IF(lbc==0, "getLayerUser(name=%p) is dead", handle.get());
- }
- return lbc;
-}
-
binder::Status Client::createSurface(const std::string& name, int32_t flags,
const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
gui::CreateSurfaceResult* outResult) {
// We rely on createLayer to check permissions.
sp<IBinder> handle;
- int32_t layerId;
- uint32_t transformHint;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
static_cast<uint32_t>(flags), std::move(metadata));
- const status_t status =
- mFlinger->createLayer(args, &handle, parent, &layerId, nullptr, &transformHint);
- if (status == NO_ERROR) {
- outResult->handle = handle;
- outResult->layerId = layerId;
- outResult->transformHint = static_cast<int32_t>(transformHint);
- }
+ args.parentHandle = parent;
+ const status_t status = mFlinger->createLayer(args, *outResult);
return binderStatusFromStatusT(status);
}
binder::Status Client::clearLayerFrameStats(const sp<IBinder>& handle) {
status_t status;
- sp<Layer> layer = getLayerUser(handle);
+ sp<Layer> layer = LayerHandle::getLayer(handle);
if (layer == nullptr) {
status = NAME_NOT_FOUND;
} else {
@@ -109,7 +74,7 @@
binder::Status Client::getLayerFrameStats(const sp<IBinder>& handle, gui::FrameStats* outStats) {
status_t status;
- sp<Layer> layer = getLayerUser(handle);
+ sp<Layer> layer = LayerHandle::getLayer(handle);
if (layer == nullptr) {
status = NAME_NOT_FOUND;
} else {
@@ -134,31 +99,21 @@
}
binder::Status Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle,
- gui::MirrorSurfaceResult* outResult) {
+ gui::CreateSurfaceResult* outResult) {
sp<IBinder> handle;
- int32_t layerId;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), "MirrorRoot",
0 /* flags */, gui::LayerMetadata());
- status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, &handle, &layerId);
- if (status == NO_ERROR) {
- outResult->handle = handle;
- outResult->layerId = layerId;
- }
+ status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, *outResult);
return binderStatusFromStatusT(status);
}
-binder::Status Client::mirrorDisplay(int64_t displayId, gui::MirrorSurfaceResult* outResult) {
+binder::Status Client::mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) {
sp<IBinder> handle;
- int32_t layerId;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this),
"MirrorRoot-" + std::to_string(displayId), 0 /* flags */,
gui::LayerMetadata());
std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
- status_t status = mFlinger->mirrorDisplay(*id, args, &handle, &layerId);
- if (status == NO_ERROR) {
- outResult->handle = handle;
- outResult->layerId = layerId;
- }
+ status_t status = mFlinger->mirrorDisplay(*id, args, *outResult);
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 4e59dfd..af410ea 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -38,12 +38,6 @@
status_t initCheck() const;
- // protected by SurfaceFlinger::mStateLock
- void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
- void detachLayer(const Layer* layer);
-
- sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
-
private:
// ISurfaceComposerClient interface
@@ -57,16 +51,13 @@
gui::FrameStats* outStats) override;
binder::Status mirrorSurface(const sp<IBinder>& mirrorFromHandle,
- gui::MirrorSurfaceResult* outResult) override;
+ gui::CreateSurfaceResult* outResult) override;
- binder::Status mirrorDisplay(int64_t displayId, gui::MirrorSurfaceResult* outResult) override;
+ binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override;
// constant
sp<SurfaceFlinger> mFlinger;
- // protected by mLock
- DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers;
-
// thread-safe
mutable Mutex mLock;
};
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index cf932a8..09e41ff 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -22,6 +22,7 @@
#include <cinttypes>
#include <android-base/stringprintf.h>
+#include <gui/TraceUtils.h>
#include <renderengine/impl/ExternalTexture.h>
#include "ClientCache.h"
@@ -36,12 +37,12 @@
ClientCacheBuffer** outClientCacheBuffer) {
auto& [processToken, id] = cacheId;
if (processToken == nullptr) {
- ALOGE("failed to get buffer, invalid (nullptr) process token");
+ ALOGE_AND_TRACE("ClientCache::getBuffer - invalid (nullptr) process token");
return false;
}
auto it = mBuffers.find(processToken);
if (it == mBuffers.end()) {
- ALOGE("failed to get buffer, invalid process token");
+ ALOGE_AND_TRACE("ClientCache::getBuffer - invalid process token");
return false;
}
@@ -49,7 +50,7 @@
auto bufItr = processBuffers.find(id);
if (bufItr == processBuffers.end()) {
- ALOGV("failed to get buffer, invalid buffer id");
+ ALOGE_AND_TRACE("ClientCache::getBuffer - invalid buffer id");
return false;
}
@@ -58,16 +59,17 @@
return true;
}
-bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
+base::expected<std::shared_ptr<renderengine::ExternalTexture>, ClientCache::AddError>
+ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
auto& [processToken, id] = cacheId;
if (processToken == nullptr) {
- ALOGE("failed to cache buffer: invalid process token");
- return false;
+ ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) process token");
+ return base::unexpected(AddError::Unspecified);
}
if (!buffer) {
- ALOGE("failed to cache buffer: invalid buffer");
- return false;
+ ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) buffer");
+ return base::unexpected(AddError::Unspecified);
}
std::lock_guard lock(mMutex);
@@ -79,16 +81,16 @@
if (it == mBuffers.end()) {
token = processToken.promote();
if (!token) {
- ALOGE("failed to cache buffer: invalid token");
- return false;
+ ALOGE_AND_TRACE("ClientCache::add - invalid token");
+ return base::unexpected(AddError::Unspecified);
}
// Only call linkToDeath if not a local binder
if (token->localBinder() == nullptr) {
status_t err = token->linkToDeath(mDeathRecipient);
if (err != NO_ERROR) {
- ALOGE("failed to cache buffer: could not link to death");
- return false;
+ ALOGE_AND_TRACE("ClientCache::add - could not link to death");
+ return base::unexpected(AddError::Unspecified);
}
}
auto [itr, success] =
@@ -102,21 +104,22 @@
auto& processBuffers = it->second.second;
if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
- ALOGE("failed to cache buffer: cache is full");
- return false;
+ ALOGE_AND_TRACE("ClientCache::add - cache is full");
+ return base::unexpected(AddError::CacheFull);
}
LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
"Attempted to build the ClientCache before a RenderEngine instance was "
"ready!");
- processBuffers[id].buffer = std::make_shared<
- renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
- renderengine::impl::ExternalTexture::Usage::
- READABLE);
- return true;
+
+ return (processBuffers[id].buffer = std::make_shared<
+ renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
+ renderengine::impl::ExternalTexture::
+ Usage::READABLE));
}
-void ClientCache::erase(const client_cache_t& cacheId) {
+sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) {
+ sp<GraphicBuffer> buffer;
auto& [processToken, id] = cacheId;
std::vector<sp<ErasedRecipient>> pendingErase;
{
@@ -124,9 +127,11 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
ALOGE("failed to erase buffer, could not retrieve buffer");
- return;
+ return nullptr;
}
+ buffer = buf->buffer->getBuffer();
+
for (auto& recipient : buf->recipients) {
sp<ErasedRecipient> erasedRecipient = recipient.promote();
if (erasedRecipient) {
@@ -140,6 +145,7 @@
for (auto& recipient : pendingErase) {
recipient->bufferErased(cacheId);
}
+ return buffer;
}
std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index a9b8177..b56b252 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -33,12 +33,27 @@
namespace android {
+// This class manages a cache of buffer handles between SurfaceFlinger clients
+// and the SurfaceFlinger process which optimizes away some of the cost of
+// sending buffer handles across processes.
+//
+// Buffers are explicitly cached and uncached by the SurfaceFlinger client. When
+// a buffer is uncached, it is not only purged from this cache, but the buffer
+// ID is also passed down to CompositionEngine to purge it from a similar cache
+// used between SurfaceFlinger and Composer HAL. The buffer ID used to purge
+// both the SurfaceFlinger side of this other cache, as well as Composer HAL's
+// side of the cache.
+//
class ClientCache : public Singleton<ClientCache> {
public:
ClientCache();
- bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
- void erase(const client_cache_t& cacheId);
+ enum class AddError { CacheFull, Unspecified };
+
+ base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add(
+ const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+
+ sp<GraphicBuffer> erase(const client_cache_t& cacheId);
std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b5d2ad0..30d34a5 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -25,7 +25,7 @@
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V4-cpp",
"libbase",
"libcutils",
"libgui",
@@ -42,7 +42,6 @@
"libmath",
"librenderengine",
"libtonemap",
- "libtrace_proto",
"libaidlcommonsupport",
"libprocessgroup",
"libcgrouprc",
@@ -141,6 +140,11 @@
"libgmock",
"libgtest",
],
+ // For some reason, libvulkan isn't picked up from librenderengine
+ // Probably ASAN related?
+ shared_libs: [
+ "libvulkan",
+ ],
sanitize: {
// By using the address sanitizer, we not only uncover any issues
// with the test, but also any issues with the code under test.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 6832ae1..7c10fa5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -56,9 +56,9 @@
virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
virtual renderengine::RenderEngine& getRenderEngine() const = 0;
- virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+ virtual void setRenderEngine(renderengine::RenderEngine*) = 0;
- virtual TimeStats& getTimeStats() const = 0;
+ virtual TimeStats* getTimeStats() const = 0;
virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
virtual bool needsAnotherUpdate() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index f861fc9..415a041 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -52,6 +52,9 @@
// All the layers that have queued updates.
Layers layersWithQueuedFrames;
+ // All graphic buffers that will no longer be used and should be removed from caches.
+ std::vector<uint64_t> bufferIdsToUncache;
+
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index fe8cad5..608c53a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -118,6 +118,9 @@
// Requested white point of the layer in nits
const float whitePointNits;
+
+ // True if layers with 170M dataspace should be overridden to sRGB.
+ const bool treat170mAsSrgb;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 974f7c6..ad98e93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -163,7 +163,6 @@
// The buffer and related state
sp<GraphicBuffer> buffer;
- int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
uint64_t frameNumber = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 874b330..bd43c89 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -23,6 +23,7 @@
#include <type_traits>
#include <unordered_map>
#include <utility>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
@@ -272,6 +273,7 @@
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+ virtual void uncacheBuffers(const std::vector<uint64_t>&) = 0;
virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 6d0c395..4dbf8d2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <optional>
#include <string>
+#include <vector>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
@@ -81,6 +82,10 @@
// TODO(lpique): Make this protected once it is only internally called.
virtual CompositionState& editState() = 0;
+ // Clear the cache entries for a set of buffers that SurfaceFlinger no
+ // longer cares about.
+ virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0;
+
// Recalculates the state of the output layer from the output-independent
// layer. If includeGeometry is false, the geometry state can be skipped.
// internalDisplayRotationFlags must be set to the rotation flags for the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index dd4dbe9..c699557 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -34,9 +34,9 @@
void setHwComposer(std::unique_ptr<HWComposer>) override;
renderengine::RenderEngine& getRenderEngine() const override;
- void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+ void setRenderEngine(renderengine::RenderEngine*) override;
- TimeStats& getTimeStats() const override;
+ TimeStats* getTimeStats() const override;
void setTimeStats(const std::shared_ptr<TimeStats>&) override;
bool needsAnotherUpdate() const override;
@@ -58,7 +58,7 @@
private:
std::unique_ptr<HWComposer> mHwComposer;
- std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+ renderengine::RenderEngine* mRenderEngine;
std::shared_ptr<TimeStats> mTimeStats;
bool mNeedsAnotherUpdate = false;
nsecs_t mRefreshStartTime = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index fd22aa3..6e9ea6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -17,7 +17,8 @@
#pragma once
#include <cstdint>
-#include <vector>
+#include <stack>
+#include <unordered_map>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -37,35 +38,76 @@
namespace compositionengine::impl {
-// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
-// HWC display and layer. When updating a display target or a layer buffer,
-// we have the option to send the buffer handle over or to request the HAL to
-// retrieve it from its cache. The latter is cheaper since it eliminates the
-// overhead to transfer the handle over the trasport layer, and the overhead
-// for the HAL to clone and retain the handle.
-//
-// To be able to find out whether a buffer is already in the HAL's cache, we
-// use HWComposerBufferCache to mirror the cache in SF.
-class HwcBufferCache {
-public:
- HwcBufferCache();
- // Given a buffer, return the HWC cache slot and
- // buffer to be sent to HWC.
- //
- // outBuffer is set to buffer when buffer is not in the HWC cache;
- // otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer);
+// The buffer cache returns both a slot and the buffer that should be sent to HWC. In cases
+// where the buffer is already cached, the buffer is a nullptr and will not be sent to HWC as
+// an optimization.
+struct HwcSlotAndBuffer {
+ uint32_t slot;
+ sp<GraphicBuffer> buffer;
+};
- // Special caching slot for the layer caching feature.
- static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+//
+// Manages the slot assignments for a buffers stored in Composer HAL's cache.
+//
+// Cache slots are an optimization when communicating buffer handles to Composer
+// HAL. When updating a layer's buffer, we can either send a new buffer handle
+// along with it's slot assignment or request the HAL to reuse a buffer handle
+// that we've already sent by using the slot assignment. The latter is cheaper
+// since it eliminates the overhead to transfer the buffer handle over IPC and
+// the overhead for the HAL to clone the handle.
+//
+class HwcBufferCache {
+private:
+ static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
+
+public:
+ // public for testing
+ // Override buffers don't use the normal cache slots because we don't want them to evict client
+ // buffers from the cache. We add an extra slot at the end for the override buffers.
+ static const constexpr size_t kOverrideBufferSlot = kMaxLayerBufferCount;
+
+ HwcBufferCache();
+
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // A special slot number is used for override buffers.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer);
+
+ //
+ // When a client process discards a buffer, it needs to be purged from the HWC cache.
+ //
+ // Returns the slot number of the buffer, or UINT32_MAX if it wasn't found in the cache.
+ //
+ uint32_t uncache(uint64_t graphicBufferId);
private:
- // an array where the index corresponds to a slot and the value corresponds to a (counter,
- // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
- // or used and allows us to keep track of the least-recently used buffer.
- static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
+ uint32_t cache(const sp<GraphicBuffer>& buffer);
+ uint32_t getLeastRecentlyUsedSlot();
+
+ struct Cache {
+ sp<GraphicBuffer> buffer;
+ uint32_t slot;
+ // Cache entries are evicted according to least-recently-used when more than
+ // kMaxLayerBufferCount unique buffers have been sent to a layer.
+ uint64_t lruCounter;
+ };
+
+ std::unordered_map<uint64_t, Cache> mCacheByBufferId;
+ sp<GraphicBuffer> mLastOverrideBuffer;
+ std::stack<uint32_t> mFreeSlots;
+ uint64_t mLeastRecentlyUsedCounter;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 23d5570..1393e29 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -82,6 +82,7 @@
void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
void present(const CompositionRefreshArgs&) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
@@ -134,9 +135,11 @@
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+ virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<LayerFE*> &outLayerFEs) override;
+ virtual bool layerNeedsFiltering(const OutputLayer*) const;
void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
@@ -153,6 +156,8 @@
bool mustRecompose() const;
+ const std::string& getNamePlusId() const { return mNamePlusId; }
+
private:
void dirtyEntireOutput();
void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&);
@@ -163,6 +168,7 @@
const compositionengine::CompositionRefreshArgs&) const;
std::string mName;
+ std::string mNamePlusId;
std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6d4abf9..f383392 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,6 +20,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
@@ -44,6 +45,8 @@
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 5aec7c2..e309442 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -72,6 +72,7 @@
SolidColor = 1u << 16,
BackgroundBlurRadius = 1u << 17,
BlurRegions = 1u << 18,
+ HasProtectedContent = 1u << 19,
};
// clang-format on
@@ -245,9 +246,9 @@
ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
- bool isProtected() const {
- return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
- }
+ wp<GraphicBuffer> getBuffer() const { return mBuffer.get(); }
+
+ bool isProtected() const { return mIsProtected.get(); }
bool hasSolidColorCompositionType() const {
return getOutputLayer()->getLayerFE().getCompositionState()->compositionType ==
@@ -482,7 +483,11 @@
return hash;
}};
- static const constexpr size_t kNumNonUniqueFields = 17;
+ OutputLayerState<bool, LayerStateField::HasProtectedContent> mIsProtected{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+ }};
+
+ static const constexpr size_t kNumNonUniqueFields = 18;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -501,7 +506,7 @@
&mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
&mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
&mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
- &mFrameNumber,
+ &mFrameNumber, &mIsProtected,
};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index a48cc6f..9b2387b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -40,9 +40,9 @@
MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
- MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+ MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*));
- MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+ MOCK_CONST_METHOD0(getTimeStats, TimeStats*());
MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 7592cac..18e6879 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -82,6 +82,7 @@
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
MOCK_METHOD2(rebuildLayerStacks,
void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD2(collectVisibleLayers,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index c22f1bf..5fef63a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -35,6 +35,8 @@
MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
+
MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index a4e1fff..15fadbc 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -65,15 +65,15 @@
}
renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
- return *mRenderEngine.get();
+ return *mRenderEngine;
}
-void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
- mRenderEngine = std::move(renderEngine);
+void CompositionEngine::setRenderEngine(renderengine::RenderEngine* renderEngine) {
+ mRenderEngine = renderEngine;
}
-TimeStats& CompositionEngine::getTimeStats() const {
- return *mTimeStats.get();
+TimeStats* CompositionEngine::getTimeStats() const {
+ return mTimeStats.get();
}
void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 0b69d44..24669c2 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -25,6 +25,7 @@
#include <compositionengine/impl/DumpHelpers.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/RenderSurface.h>
+#include <gui/TraceUtils.h>
#include <utils/Trace.h>
@@ -235,7 +236,7 @@
bool Display::chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str());
ALOGV(__FUNCTION__);
if (mIsDisconnected) {
@@ -248,12 +249,17 @@
return false;
}
- const TimePoint startTime = TimePoint::now();
-
// 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();
+
+ if (isPowerHintSessionEnabled()) {
+ mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+ }
+
+ const TimePoint hwcValidateStartTime = TimePoint::now();
+
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
getState().earliestPresentTime,
@@ -266,8 +272,10 @@
}
if (isPowerHintSessionEnabled()) {
- mPowerAdvisor->setHwcValidateTiming(mId, startTime, TimePoint::now());
- mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+ mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
+ if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+ }
}
return true;
@@ -432,13 +440,6 @@
}
impl::Output::finishFrame(refreshArgs, std::move(result));
-
- if (isPowerHintSessionEnabled()) {
- auto& hwc = getCompositionEngine().getHwComposer();
- if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
- mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
- }
- }
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f95382d..d64fd57 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -16,43 +16,80 @@
#include <compositionengine/impl/HwcBufferCache.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <gui/BufferQueue.h>
#include <ui/GraphicBuffer.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-
namespace android::compositionengine::impl {
HwcBufferCache::HwcBufferCache() {
- std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr));
+ for (uint32_t i = 0; i < kMaxLayerBufferCount; i++) {
+ mFreeSlots.push(i);
+ }
}
-void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- // default is 0
- if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
- slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
- *outSlot = 0;
- } else {
- *outSlot = static_cast<uint32_t>(slot);
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+ // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send
+ // in nullptrs.
+ if (buffer == nullptr) {
+ return {0, nullptr};
}
-
- auto& currentBuffer = mBuffers[*outSlot];
- wp<GraphicBuffer> weakCopy(buffer);
- if (currentBuffer == weakCopy) {
- // already cached in HWC, skip sending the buffer
- *outBuffer = nullptr;
- } else {
- *outBuffer = buffer;
-
- // update cache
- currentBuffer = buffer;
+ if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
+ Cache& cache = i->second;
+ // mark this cache slot as more recently used so it won't get evicted anytime soon
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ return {cache.slot, nullptr};
}
+ return {cache(buffer), buffer};
+}
+
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer) {
+ if (buffer == mLastOverrideBuffer) {
+ return {kOverrideBufferSlot, nullptr};
+ }
+ mLastOverrideBuffer = buffer;
+ return {kOverrideBufferSlot, buffer};
+}
+
+uint32_t HwcBufferCache::uncache(uint64_t bufferId) {
+ if (auto i = mCacheByBufferId.find(bufferId); i != mCacheByBufferId.end()) {
+ uint32_t slot = i->second.slot;
+ mCacheByBufferId.erase(i);
+ mFreeSlots.push(slot);
+ return slot;
+ }
+ if (mLastOverrideBuffer && bufferId == mLastOverrideBuffer->getId()) {
+ mLastOverrideBuffer = nullptr;
+ return kOverrideBufferSlot;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t HwcBufferCache::cache(const sp<GraphicBuffer>& buffer) {
+ Cache cache;
+ cache.slot = getLeastRecentlyUsedSlot();
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ cache.buffer = buffer;
+ mCacheByBufferId.emplace(buffer->getId(), cache);
+ return cache.slot;
+}
+
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+ if (mFreeSlots.empty()) {
+ assert(!mCacheByBufferId.empty());
+ // evict the least recently used cache entry
+ auto cacheToErase = mCacheByBufferId.begin();
+ for (auto i = cacheToErase; i != mCacheByBufferId.end(); ++i) {
+ if (i->second.lruCounter < cacheToErase->second.lruCounter) {
+ cacheToErase = i;
+ }
+ }
+ uint32_t slot = cacheToErase->second.slot;
+ mCacheByBufferId.erase(cacheToErase);
+ mFreeSlots.push(slot);
+ }
+ uint32_t slot = mFreeSlots.top();
+ mFreeSlots.pop();
+ return slot;
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 6631a27..a405c4d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -106,7 +106,6 @@
dumpVal(out, "composition type", toString(compositionType), compositionType);
out.append("\n buffer: ");
- dumpVal(out, "slot", bufferSlot);
dumpVal(out, "buffer", buffer.get());
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index e3f3680..16ef812 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -29,6 +29,7 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
#include <ftl/future.h>
+#include <gui/TraceUtils.h>
#include <thread>
@@ -116,6 +117,9 @@
void Output::setName(const std::string& name) {
mName = name;
+ auto displayIdOpt = getDisplayId();
+ mNamePlusId = base::StringPrintf("%s (%s)", mName.c_str(),
+ displayIdOpt ? to_string(*displayIdOpt).c_str() : "NA");
}
void Output::setCompositionEnabled(bool enabled) {
@@ -424,10 +428,11 @@
ALOGV(__FUNCTION__);
rebuildLayerStacks(refreshArgs, geomSnapshots);
+ uncacheBuffers(refreshArgs.bufferIdsToUncache);
}
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
@@ -451,6 +456,15 @@
renderCachedSets(refreshArgs);
}
+void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ if (bufferIdsToUncache.empty()) {
+ return;
+ }
+ for (auto outputLayer : getOutputLayersOrderedByZ()) {
+ outputLayer->uncacheBuffers(bufferIdsToUncache);
+ }
+}
+
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
ATRACE_CALL();
@@ -900,6 +914,13 @@
ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB;
*outHdrDataSpace = ui::Dataspace::UNKNOWN;
+ // An Output's layers may be stale when it is disabled. As a consequence, the layers returned by
+ // getOutputLayersOrderedByZ may not be in a valid state and it is not safe to access their
+ // properties. Return a default dataspace value in this case.
+ if (!getState().isEnabled) {
+ return ui::Dataspace::V0_SRGB;
+ }
+
for (const auto* layer : getOutputLayersOrderedByZ()) {
switch (layer->getLayerFE().getCompositionState()->dataspace) {
case ui::Dataspace::V0_SCRGB:
@@ -1171,15 +1192,9 @@
bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
return layer->getLayerFE().getCompositionState()->hasProtectedContent;
});
- if (needsProtected != renderEngine.isProtected()) {
- renderEngine.useProtectedContext(needsProtected);
- }
- if (needsProtected != mRenderSurface->isProtected() &&
- needsProtected == renderEngine.isProtected()) {
+ if (needsProtected != mRenderSurface->isProtected()) {
mRenderSurface->setProtected(needsProtected);
}
- } else if (!outputState.isSecure && renderEngine.isProtected()) {
- renderEngine.useProtectedContext(false);
}
}
@@ -1225,40 +1240,8 @@
ALOGV("hasClientComposition");
- renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
- clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
- clientCompositionDisplay.orientation =
- ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
- clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
- ? outputState.dataspace
- : ui::Dataspace::UNKNOWN;
-
- // If we have a valid current display brightness use that, otherwise fall back to the
- // display's max desired
- clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f
- ? outputState.displayBrightnessNits
- : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.maxLuminance =
- mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.targetLuminanceNits =
- outputState.clientTargetBrightness * outputState.displayBrightnessNits;
- clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage;
- clientCompositionDisplay.renderIntent =
- static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(
- outputState.renderIntent);
-
- // Compute the global color transform matrix.
- clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
- for (auto& info : outputState.borderInfoList) {
- renderengine::BorderRenderInfo borderInfo;
- borderInfo.width = info.width;
- borderInfo.color = info.color;
- borderInfo.combinedRegion = info.combinedRegion;
- clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo));
- }
- clientCompositionDisplay.deviceHandlesColorTransform =
- outputState.usesDeviceComposition || getSkipColorTransform();
+ renderengine::DisplaySettings clientCompositionDisplay =
+ generateClientCompositionDisplaySettings();
// Generate the client composition requests for the layers on this output.
auto& renderEngine = getCompositionEngine().getRenderEngine();
@@ -1333,10 +1316,13 @@
const auto fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
- if (auto& timeStats = getCompositionEngine().getTimeStats(); fence->isValid()) {
- timeStats.recordRenderEngineDuration(renderEngineStart, std::make_shared<FenceTime>(fence));
- } else {
- timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+ if (auto timeStats = getCompositionEngine().getTimeStats()) {
+ if (fence->isValid()) {
+ timeStats->recordRenderEngineDuration(renderEngineStart,
+ std::make_shared<FenceTime>(fence));
+ } else {
+ timeStats->recordRenderEngineDuration(renderEngineStart, systemTime());
+ }
}
for (auto* clientComposedLayer : clientCompositionLayersFE) {
@@ -1346,6 +1332,47 @@
return base::unique_fd(fence->dup());
}
+renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings() const {
+ const auto& outputState = getState();
+
+ renderengine::DisplaySettings clientCompositionDisplay;
+ clientCompositionDisplay.namePlusId = mNamePlusId;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
+ clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
+ clientCompositionDisplay.orientation =
+ ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
+ clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
+ ? outputState.dataspace
+ : ui::Dataspace::UNKNOWN;
+
+ // If we have a valid current display brightness use that, otherwise fall back to the
+ // display's max desired
+ clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f
+ ? outputState.displayBrightnessNits
+ : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+ clientCompositionDisplay.maxLuminance =
+ mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+ clientCompositionDisplay.targetLuminanceNits =
+ outputState.clientTargetBrightness * outputState.displayBrightnessNits;
+ clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage;
+ clientCompositionDisplay.renderIntent =
+ static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(
+ outputState.renderIntent);
+
+ // Compute the global color transform matrix.
+ clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
+ for (auto& info : outputState.borderInfoList) {
+ renderengine::BorderRenderInfo borderInfo;
+ borderInfo.width = info.width;
+ borderInfo.color = info.color;
+ borderInfo.combinedRegion = info.combinedRegion;
+ clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo));
+ }
+ clientCompositionDisplay.deviceHandlesColorTransform =
+ outputState.usesDeviceComposition || getSkipColorTransform();
+ return clientCompositionDisplay;
+}
+
std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*>& outLayerFEs) {
std::vector<LayerFE::LayerSettings> clientCompositionLayers;
@@ -1411,7 +1438,7 @@
Enabled);
compositionengine::LayerFE::ClientCompositionTargetSettings
targetSettings{.clip = clip,
- .needsFiltering = layer->needsFiltering() ||
+ .needsFiltering = layerNeedsFiltering(layer) ||
outputState.needsFiltering,
.isSecure = outputState.isSecure,
.supportsProtectedContent = supportsProtectedContent,
@@ -1420,7 +1447,8 @@
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
.blurSetting = blurSetting,
- .whitePointNits = layerState.whitePointNits};
+ .whitePointNits = layerState.whitePointNits,
+ .treat170mAsSrgb = outputState.treat170mAsSrgb};
if (auto clientCompositionSettings =
layerFE.prepareClientComposition(targetSettings)) {
clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
@@ -1441,6 +1469,10 @@
return clientCompositionLayers;
}
+bool Output::layerNeedsFiltering(const compositionengine::OutputLayer* layer) const {
+ return layer->needsFiltering();
+}
+
void Output::appendRegionFlashRequests(
const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
if (flashRegion.isEmpty()) {
@@ -1471,7 +1503,7 @@
}
void Output::postFramebuffer() {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
if (!getState().isEnabled) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index a39c527..a7c24b6 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -610,6 +610,19 @@
}
}
+void OutputLayer::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ auto& state = editState();
+ // Skip doing this if there is no HWC interface
+ if (!state.hwc) {
+ return;
+ }
+
+ for (auto bufferId : bufferIdsToUncache) {
+ state.hwc->hwcBufferCache.uncache(bufferId);
+ // TODO(b/258196272): send uncache requests to Composer HAL
+ }
+}
+
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
@@ -622,27 +635,24 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- sp<GraphicBuffer> buffer = outputIndependentState.buffer;
- sp<Fence> acquireFence = outputIndependentState.acquireFence;
- int slot = outputIndependentState.bufferSlot;
+ HwcSlotAndBuffer hwcSlotAndBuffer;
+ sp<Fence> hwcFence;
+ // Override buffers use a special cache slot so that they don't evict client buffers.
if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
- buffer = getState().overrideInfo.buffer->getBuffer();
- acquireFence = getState().overrideInfo.acquireFence;
- slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
+ hwcSlotAndBuffer = editState().hwc->hwcBufferCache.getHwcSlotAndBufferForOverride(
+ getState().overrideInfo.buffer->getBuffer());
+ hwcFence = getState().overrideInfo.acquireFence;
+ } else {
+ hwcSlotAndBuffer =
+ editState().hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
+ hwcFence = outputIndependentState.acquireFence;
}
- ALOGV("Writing buffer %p", buffer.get());
-
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- // We need access to the output-dependent state for the buffer cache there,
- // though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
-
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+ hwcSlotAndBuffer.buffer->handle, to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 0731d48..ed9a88d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -411,8 +411,8 @@
if (mLayers.size() == 1) {
base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
- if (auto* buffer = mLayers[0].getBuffer().get()) {
- base::StringAppendF(&result, " Buffer %p", buffer);
+ if (const sp<GraphicBuffer> buffer = mLayers[0].getState()->getBuffer().promote()) {
+ base::StringAppendF(&result, " Buffer %p", buffer.get());
base::StringAppendF(&result, " Format %s",
decodePixelFormat(buffer->getPixelFormat()).c_str());
}
@@ -422,8 +422,8 @@
result.append(" Cached set of:\n");
for (const Layer& layer : mLayers) {
base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str());
- if (auto* buffer = layer.getBuffer().get()) {
- base::StringAppendF(&result, " Buffer %p", buffer);
+ if (const sp<GraphicBuffer> buffer = layer.getState()->getBuffer().promote()) {
+ base::StringAppendF(&result, " Buffer %p", buffer.get());
base::StringAppendF(&result, " Format[%s]",
decodePixelFormat(buffer->getPixelFormat()).c_str());
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index b570979..60ed660 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -62,17 +62,16 @@
}
TEST_F(CompositionEngineTest, canSetRenderEngine) {
- renderengine::mock::RenderEngine* renderEngine =
- new StrictMock<renderengine::mock::RenderEngine>();
- mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine));
+ auto renderEngine = std::make_unique<StrictMock<renderengine::mock::RenderEngine>>();
+ mEngine.setRenderEngine(renderEngine.get());
- EXPECT_EQ(renderEngine, &mEngine.getRenderEngine());
+ EXPECT_EQ(renderEngine.get(), &mEngine.getRenderEngine());
}
TEST_F(CompositionEngineTest, canSetTimeStats) {
mEngine.setTimeStats(mTimeStats);
- EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+ EXPECT_EQ(mTimeStats.get(), mEngine.getTimeStats());
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index 7197780..cf72e8f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -22,66 +22,172 @@
namespace android::compositionengine {
namespace {
-class TestableHwcBufferCache : public impl::HwcBufferCache {
-public:
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
- }
-};
+using impl::HwcBufferCache;
+using impl::HwcSlotAndBuffer;
class HwcBufferCacheTest : public testing::Test {
public:
~HwcBufferCacheTest() override = default;
- void testSlot(const int inSlot, const uint32_t expectedSlot) {
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer1, outBuffer);
-
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer2, outBuffer);
-
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
- }
-
- impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
sp<GraphicBuffer> mBuffer2 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
};
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
- testSlot(0, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot);
+ EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2);
}
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
- testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
}
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
- testSlot(-123, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = 0;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ ASSERT_GT(finalCachedBufferIndex, 1);
+ // the final cached buffer has the same slot value as the oldest buffer
+ EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot);
+ // the oldest buffer is no longer in the cache because it was evicted
+ EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+
+ // the 1st buffer should be found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot);
+ // since the 1st buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+ // the 2nd buffer should be still found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot);
+ // since the 2nd buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenCached_returnsSameSlotAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
+}
+
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenSlotsFull_returnsIndependentSlot) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = -1;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ // expect to have cached at least a few buffers before evicting
+ ASSERT_GT(finalCachedBufferIndex, 1);
+
+ sp<GraphicBuffer> overrideBuffer =
+ sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ HwcSlotAndBuffer overrideSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(overrideBuffer);
+ // expect us to have a slot number
+ EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX);
+ // expect this to be the first time we cached the buffer
+ EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr);
+
+ // expect the slot number to not equal any other slot number, even after the slots have been
+ // exhausted, indicating that the override buffer slot is independent from the slots for
+ // non-override buffers
+ for (int i = 0; i < finalCachedBufferIndex; ++i) {
+ EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot);
+ }
+ // the override buffer is independently uncached from the oldest cached buffer
+ // expect to find the override buffer still in the override buffer slot
+ EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot);
+ // expect that the first buffer was not evicted from the cache when the override buffer was
+ // cached
+ EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot);
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9102139..33caa7a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -139,6 +139,8 @@
MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
(const, override));
MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
+ MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
+ getOverlaySupport, (), (const, override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e220541..c555b39 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -34,7 +34,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
- MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 0f7dce8..03cf292 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -774,7 +774,7 @@
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
- static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
+ static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::kOverrideBufferSlot;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
static constexpr float kWhitePointNits = 200.f;
@@ -823,7 +823,6 @@
mLayerFEState.hdrMetadata = kHdrMetadata;
mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
mLayerFEState.buffer = kBuffer;
- mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index eb209e9..bfd863b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1192,14 +1192,49 @@
compositionengine::LayerFESet&));
};
+ OutputPrepareTest() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+
+ mRefreshArgs.layers.push_back(mLayer1.layerFE);
+ mRefreshArgs.layers.push_back(mLayer2.layerFE);
+ }
+
+ struct Layer {
+ StrictMock<mock::OutputLayer> outputLayer;
+ sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
+ };
+
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
LayerFESet mGeomSnapshots;
+ Layer mLayer1;
+ Layer mLayer2;
};
-TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+TEST_F(OutputPrepareTest, callsUncacheBuffersOnEachOutputLayerAndThenRebuildsLayerStacks) {
InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {1, 3, 5};
+
EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+
+ mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputPrepareTest, skipsUncacheBuffersIfEmptyAndThenRebuildsLayerStacks) {
+ InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {};
+
+ EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(_)).Times(0);
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(_)).Times(0);
mOutput.prepare(mRefreshArgs, mGeomSnapshots);
}
@@ -2081,6 +2116,7 @@
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ mOutput.editState().isEnabled = true;
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
.WillRepeatedly(Return(&mLayer1.mOutputLayer));
@@ -3331,8 +3367,7 @@
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
- EXPECT_CALL(mCompositionEngine, getTimeStats())
- .WillRepeatedly(ReturnRef(*mTimeStats.get()));
+ EXPECT_CALL(mCompositionEngine, getTimeStats()).WillRepeatedly(Return(mTimeStats.get()));
EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
.WillRepeatedly(ReturnRef(kHdrCapabilities));
}
@@ -4009,39 +4044,11 @@
Layer mLayer2;
};
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) {
- mOutput.mState.isSecure = false;
- mLayer2.mLayerFEState.hasProtectedContent = true;
- EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
- EXPECT_CALL(mRenderEngine, useProtectedContext(false));
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
- mOutput.mState.isSecure = true;
- mLayer2.mLayerFEState.hasProtectedContent = true;
- EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
mOutput.mState.isSecure = true;
mLayer2.mLayerFEState.hasProtectedContent = false;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
- EXPECT_CALL(mRenderEngine, useProtectedContext(false));
EXPECT_CALL(*mRenderSurface, setProtected(false));
base::unique_fd fd;
@@ -4059,10 +4066,7 @@
// For this test, we also check the call order of key functions.
InSequence seq;
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
- EXPECT_CALL(mRenderEngine, useProtectedContext(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
EXPECT_CALL(*mRenderSurface, setProtected(true));
// Must happen after setting the protected content state.
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
@@ -4080,7 +4084,6 @@
mOutput.mState.isSecure = true;
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
base::unique_fd fd;
@@ -4090,43 +4093,11 @@
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
}
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
- mOutput.mState.isSecure = true;
- mLayer2.mLayerFEState.hasProtectedContent = true;
- EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false));
- EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
- EXPECT_CALL(mRenderEngine, useProtectedContext(true));
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
- mOutput.mState.isSecure = true;
- mLayer2.mLayerFEState.hasProtectedContent = true;
- EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true));
- EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
- EXPECT_CALL(*mRenderSurface, setProtected(true));
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
mOutput.mState.isSecure = true;
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
- EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
- EXPECT_CALL(mRenderEngine, useProtectedContext(true));
base::unique_fd fd;
std::shared_ptr<renderengine::ExternalTexture> tex;
@@ -4464,6 +4435,7 @@
true /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4476,6 +4448,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -4516,6 +4489,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
@@ -4528,6 +4502,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
@@ -4540,6 +4515,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4570,6 +4546,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4582,6 +4559,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4594,6 +4572,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4624,6 +4603,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4636,6 +4616,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4648,6 +4629,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4677,6 +4659,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4689,6 +4672,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4701,6 +4685,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4728,6 +4713,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4740,6 +4726,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4752,6 +4739,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
@@ -4936,6 +4924,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4954,6 +4943,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4987,6 +4977,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
LayerFE::LayerSettings mShadowSettings;
@@ -5029,6 +5020,7 @@
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
kLayerWhitePointNits,
+ false /* treat170mAsSrgb */,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
diff --git a/services/surfaceflinger/Display/DisplayMap.h b/services/surfaceflinger/Display/DisplayMap.h
index baf0da9..0d59706 100644
--- a/services/surfaceflinger/Display/DisplayMap.h
+++ b/services/surfaceflinger/Display/DisplayMap.h
@@ -17,6 +17,7 @@
#pragma once
#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
namespace android::display {
@@ -28,4 +29,7 @@
template <typename Key, typename Value>
using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+template <typename T>
+using PhysicalDisplayVector = ftl::SmallVector<T, 3>;
+
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
new file mode 100644
index 0000000..d07cdf5
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <ftl/non_null.h>
+
+#include <scheduler/FrameRateMode.h>
+
+namespace android::display {
+
+struct DisplayModeRequest {
+ scheduler::FrameRateMode mode;
+
+ // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
+ bool emitEvent = false;
+};
+
+inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) {
+ return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent;
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp
index b4f104a..0c7a58e 100644
--- a/services/surfaceflinger/Display/DisplaySnapshot.cpp
+++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp
@@ -14,11 +14,13 @@
* limitations under the License.
*/
+#include <algorithm>
#include <functional>
#include <utility>
#include <ftl/algorithm.h>
#include <ftl/enum.h>
+#include <ui/DebugUtils.h>
#include "DisplaySnapshot.h"
@@ -26,11 +28,12 @@
DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId,
ui::DisplayConnectionType connectionType,
- DisplayModes&& displayModes,
+ DisplayModes&& displayModes, ui::ColorModes&& colorModes,
std::optional<DeviceProductInfo>&& deviceProductInfo)
: mDisplayId(displayId),
mConnectionType(connectionType),
mDisplayModes(std::move(displayModes)),
+ mColorModes(std::move(colorModes)),
mDeviceProductInfo(std::move(deviceProductInfo)) {}
std::optional<DisplayModeId> DisplaySnapshot::translateModeId(hal::HWConfigId hwcId) const {
@@ -41,18 +44,35 @@
.transform(&ftl::to_key<DisplayModes>);
}
-void DisplaySnapshot::dump(std::string& out) const {
- using namespace std::string_literals;
+ui::ColorModes DisplaySnapshot::filterColorModes(bool supportsWideColor) const {
+ ui::ColorModes modes = mColorModes;
- out += " connectionType="s;
- out += ftl::enum_string(mConnectionType);
-
- out += "\n deviceProductInfo="s;
- if (mDeviceProductInfo) {
- mDeviceProductInfo->dump(out);
- } else {
- out += "{}"s;
+ // If the display is internal and the configuration claims it's not wide color capable, filter
+ // out all wide color modes. The typical reason why this happens is that the hardware is not
+ // good enough to support GPU composition of wide color, and thus the OEMs choose to disable
+ // this capability.
+ if (mConnectionType == ui::DisplayConnectionType::Internal && !supportsWideColor) {
+ const auto it = std::remove_if(modes.begin(), modes.end(), ui::isWideColorMode);
+ modes.erase(it, modes.end());
}
+
+ return modes;
+}
+
+void DisplaySnapshot::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
+
+ dumper.dump("connectionType"sv, ftl::enum_string(mConnectionType));
+
+ dumper.dump("colorModes"sv);
+ {
+ utils::Dumper::Indent indent(dumper);
+ for (const auto mode : mColorModes) {
+ dumper.dump({}, decodeColorMode(mode));
+ }
+ }
+
+ dumper.dump("deviceProductInfo"sv, mDeviceProductInfo);
}
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h
index 0279220..23471f5 100644
--- a/services/surfaceflinger/Display/DisplaySnapshot.h
+++ b/services/surfaceflinger/Display/DisplaySnapshot.h
@@ -17,19 +17,20 @@
#pragma once
#include <optional>
-#include <string>
+#include <ui/ColorMode.h>
#include <ui/DisplayId.h>
#include <ui/StaticDisplayInfo.h>
-#include "../DisplayHardware/DisplayMode.h"
+#include "DisplayHardware/DisplayMode.h"
+#include "Utils/Dumper.h"
namespace android::display {
// Immutable state of a physical display, captured on hotplug.
class DisplaySnapshot {
public:
- DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&,
+ DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&, ui::ColorModes&&,
std::optional<DeviceProductInfo>&&);
DisplaySnapshot(const DisplaySnapshot&) = delete;
@@ -41,9 +42,12 @@
std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
const auto& displayModes() const { return mDisplayModes; }
+ const auto& colorModes() const { return mColorModes; }
const auto& deviceProductInfo() const { return mDeviceProductInfo; }
- void dump(std::string&) const;
+ ui::ColorModes filterColorModes(bool supportsWideColor) const;
+
+ void dump(utils::Dumper&) const;
private:
const PhysicalDisplayId mDisplayId;
@@ -51,6 +55,7 @@
// Effectively const except in move constructor.
DisplayModes mDisplayModes;
+ ui::ColorModes mColorModes;
std::optional<DeviceProductInfo> mDeviceProductInfo;
};
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f8115eb..46b857b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -41,6 +41,7 @@
#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
+#include "FrontEnd/DisplayInfo.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
#include "SurfaceFlinger.h"
@@ -67,9 +68,10 @@
mCompositionDisplay{args.compositionDisplay},
mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
+ mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary),
- mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
+ mRefreshRateSelector(std::move(args.refreshRateSelector)) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->createRenderSurface(
compositionengine::RenderSurfaceCreationArgsBuilder()
@@ -103,7 +105,9 @@
mCompositionDisplay->getRenderSurface()->initialize();
- if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
+ if (const auto powerModeOpt = args.initialPowerMode) {
+ setPowerMode(*powerModeOpt);
+ }
// initialize the display orientation transform.
setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -131,7 +135,7 @@
}
}
-auto DisplayDevice::getInputInfo() const -> InputInfo {
+auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo {
gui::DisplayInfo info;
info.displayId = getLayerStack().id;
@@ -160,7 +164,9 @@
return {.info = info,
.transform = displayTransform,
.receivesInput = receivesInput(),
- .isSecure = isSecure()};
+ .isSecure = isSecure(),
+ .isPrimary = isPrimary(),
+ .rotationFlags = ui::Transform::toRotationFlags(mOrientation)};
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
@@ -175,8 +181,7 @@
mPowerMode = mode;
- getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
- *mPowerMode != hal::PowerMode::OFF);
+ getCompositionDisplay()->setCompositionEnabled(isPoweredOn());
}
void DisplayDevice::enableLayerCaching(bool enable) {
@@ -191,36 +196,32 @@
return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
}
-void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) {
- const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
- [](const DisplayModePtr& mode) { return mode->getFps(); });
+void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
+ ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue());
+ ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
- LOG_ALWAYS_FATAL_IF(!fpsOpt, "Unknown mode");
- const Fps fps = *fpsOpt;
-
- ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
-
- mRefreshRateConfigs->setActiveModeId(modeId);
+ mRefreshRateSelector->setActiveMode(modeId, renderFps);
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(fps);
+ mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
}
}
status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) {
- if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) {
+ if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) {
ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
- info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null",
+ info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str()
+ : "null",
to_string(getId()).c_str());
return BAD_VALUE;
}
- mNumModeSwitchesInPolicy++;
mUpcomingActiveMode = info;
- ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
- return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
- constraints, outTimeline);
+ ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue());
+ return mHwComposer.setActiveModeWithConstraints(getPhysicalId(),
+ info.modeOpt->modePtr->getHwcId(), constraints,
+ outTimeline);
}
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -235,7 +236,7 @@
return vsyncPeriod;
}
- return refreshRateConfigs().getActiveModePtr()->getVsyncPeriod();
+ return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod();
}
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -314,31 +315,14 @@
return sPrimaryDisplayRotationFlags;
}
-std::string DisplayDevice::getDebugName() const {
- using namespace std::string_literals;
+void DisplayDevice::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
- std::string name = "Display "s + to_string(getId()) + " ("s;
+ dumper.dump("name"sv, '"' + mDisplayName + '"');
+ dumper.dump("powerMode"sv, mPowerMode);
- name += isVirtual() ? "virtual"s : "physical"s;
-
- if (isPrimary()) {
- name += ", primary"s;
- }
-
- return name + ", \""s + mDisplayName + "\")"s;
-}
-
-void DisplayDevice::dump(std::string& result) const {
- using namespace std::string_literals;
-
- result += getDebugName();
-
- result += "\n powerMode="s;
- result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)";
- result += '\n';
-
- if (mRefreshRateConfigs) {
- mRefreshRateConfigs->dump(result);
+ if (mRefreshRateSelector) {
+ mRefreshRateSelector->dump(dumper);
}
}
@@ -426,26 +410,35 @@
capabilities.getDesiredMinLuminance());
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
}
- const auto fpsRange = mRefreshRateConfigs->getSupportedRefreshRateRange();
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+ ftl::Flags<RefreshRateOverlay::Features> features;
+ if (showSpinner) {
+ features |= RefreshRateOverlay::Features::Spinner;
+ }
+
+ if (showRenderRate) {
+ features |= RefreshRateOverlay::Features::RenderRate;
+ }
+
+ const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- mRefreshRateOverlay->changeRefreshRate(getActiveMode().getFps());
+ mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
}
bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
bool timerExpired) {
- if (mRefreshRateConfigs && mRefreshRateOverlay) {
- const auto newRefreshRate =
- mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
- if (newRefreshRate) {
- mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+ if (mRefreshRateSelector && mRefreshRateOverlay) {
+ const auto newMode =
+ mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
+ if (newMode) {
+ mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
return true;
}
}
@@ -459,13 +452,14 @@
}
}
-bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) {
+auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction {
ATRACE_CALL();
- LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
- LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch");
+ LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
+ LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(),
+ "DisplayId mismatch");
- ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str());
+ ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str());
std::scoped_lock lock(mActiveModeLock);
if (mDesiredActiveModeChanged) {
@@ -473,18 +467,25 @@
const auto prevConfig = mDesiredActiveMode.event;
mDesiredActiveMode = info;
mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
- return false;
+ return DesiredActiveModeAction::None;
}
+ const auto& desiredMode = *info.modeOpt->modePtr;
+
// Check if we are already at the desired mode
- if (refreshRateConfigs().getActiveModePtr()->getId() == info.mode->getId()) {
- return false;
+ if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
+ if (refreshRateSelector().getActiveMode() == info.modeOpt) {
+ return DesiredActiveModeAction::None;
+ }
+
+ setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps);
+ return DesiredActiveModeAction::InitiateRenderRateSwitch;
}
// Initiate a mode change.
mDesiredActiveModeChanged = true;
mDesiredActiveMode = info;
- return true;
+ return DesiredActiveModeAction::InitiateDisplayModeSwitch;
}
std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const {
@@ -499,27 +500,6 @@
mDesiredActiveModeChanged = false;
}
-status_t DisplayDevice::setRefreshRatePolicy(
- const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
- const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
- const status_t setPolicyResult = overridePolicy
- ? mRefreshRateConfigs->setOverridePolicy(policy)
- : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
-
- if (setPolicyResult == OK) {
- const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
-
- ALOGI("Display %s policy changed\n"
- "Previous: {%s}\n"
- "Current: {%s}\n"
- "%d mode changes were performed under the previous policy",
- to_string(getId()).c_str(), oldPolicy.toString().c_str(),
- policy ? policy->toString().c_str() : "null", numModeChanges);
- }
-
- return setPolicyResult;
-}
-
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
} // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 510df81..8f9b2a1 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -41,13 +41,15 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include "Display/DisplayModeRequest.h"
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "Scheduler/RefreshRateSelector.h"
#include "ThreadContext.h"
#include "TracedOrdinal.h"
-
+#include "Utils/Dumper.h"
namespace android {
class Fence;
@@ -165,14 +167,7 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
- struct InputInfo {
- gui::DisplayInfo info;
- ui::Transform transform;
- bool receivesInput;
- bool isSecure;
- };
-
- InputInfo getInputInfo() const;
+ surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const;
/* ------------------------------------------------------------------------
* Display power mode management.
@@ -189,64 +184,70 @@
/* ------------------------------------------------------------------------
* Display mode management.
*/
+
+ // TODO(b/241285876): Replace ActiveModeInfo and DisplayModeEvent with DisplayModeRequest.
struct ActiveModeInfo {
- DisplayModePtr mode;
- scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
+ using Event = scheduler::DisplayModeEvent;
+
+ ActiveModeInfo() = default;
+ ActiveModeInfo(scheduler::FrameRateMode mode, Event event)
+ : modeOpt(std::move(mode)), event(event) {}
+
+ explicit ActiveModeInfo(display::DisplayModeRequest&& request)
+ : ActiveModeInfo(std::move(request.mode),
+ request.emitEvent ? Event::Changed : Event::None) {}
+
+ ftl::Optional<scheduler::FrameRateMode> modeOpt;
+ Event event = Event::None;
bool operator!=(const ActiveModeInfo& other) const {
- return mode != other.mode || event != other.event;
+ return modeOpt != other.modeOpt || event != other.event;
}
};
- bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+ enum class DesiredActiveModeAction {
+ None,
+ InitiateDisplayModeSwitch,
+ InitiateRenderRateSwitch
+ };
+ DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
return mUpcomingActiveMode;
}
- const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) {
- return mRefreshRateConfigs->getActiveMode();
+ scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
+ return mRefreshRateSelector->getActiveMode();
}
- // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
- void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext);
+ void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps);
status_t initiateModeChange(const ActiveModeInfo&,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline)
REQUIRES(kMainThreadContext);
- // Returns the refresh rate configs for this display.
- scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
+ scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
- // Returns a shared pointer to the refresh rate configs for this display.
- // Clients can store this refresh rate configs and use it even if the DisplayDevice
- // is destroyed.
- std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const {
- return mRefreshRateConfigs;
+ // Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
+ std::shared_ptr<scheduler::RefreshRateSelector> holdRefreshRateSelector() const {
+ return mRefreshRateSelector;
}
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
+ void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate)
+ REQUIRES(kMainThreadContext);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
void animateRefreshRateOverlay();
nsecs_t getVsyncPeriodFromHWC() const;
- status_t setRefreshRatePolicy(
- const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
- bool overridePolicy);
-
// release HWC resources (if any) for removable displays
void disconnect();
- /* ------------------------------------------------------------------------
- * Debugging
- */
- std::string getDebugName() const;
- void dump(std::string& result) const;
+ void dump(utils::Dumper&) const;
private:
const sp<SurfaceFlinger> mFlinger;
@@ -259,6 +260,7 @@
std::string mDisplayName;
std::string mActiveModeFPSTrace;
std::string mActiveModeFPSHwcTrace;
+ std::string mRenderFrameRateFPSTrace;
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
@@ -278,7 +280,7 @@
std::vector<ui::Hdr> mOverrideHdrTypes;
- std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+ std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
mutable std::mutex mActiveModeLock;
@@ -286,8 +288,6 @@
TracedOrdinal<bool> mDesiredActiveModeChanged
GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
-
- std::atomic_int mNumModeSwitchesInPolicy = 0;
};
struct DisplayDeviceState {
@@ -330,7 +330,7 @@
HWComposer& hwComposer;
const wp<IBinder> displayToken;
const std::shared_ptr<compositionengine::Display> compositionDisplay;
- std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs;
+ std::shared_ptr<scheduler::RefreshRateSelector> refreshRateSelector;
int32_t sequenceId{0};
bool isSecure{false};
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 79dcd15..800e36d 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -55,6 +55,7 @@
using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
+using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
@@ -229,6 +230,8 @@
return;
}
+ addReader(translate<Display>(kSingleReaderKey));
+
ALOGI("Loaded AIDL composer3 HAL service");
}
@@ -264,17 +267,21 @@
}
std::string str;
+ // Use other thread to read pipe to prevent
+ // pipe is full, making HWC be blocked in writing.
+ std::thread t([&]() {
+ base::ReadFdToString(pipefds[0], &str);
+ });
const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0);
// Close the write-end of the pipe to make sure that when reading from the
// read-end we will get eof instead of blocking forever
close(pipefds[1]);
- if (status == STATUS_OK) {
- base::ReadFdToString(pipefds[0], &str);
- } else {
+ if (status != STATUS_OK) {
ALOGE("dumpDebugInfo: dump failed: %d", status);
}
+ t.join();
close(pipefds[0]);
return str;
}
@@ -293,12 +300,19 @@
}
}
-void AidlComposer::resetCommands() {
- mWriter.reset();
+void AidlComposer::resetCommands(Display display) {
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().reset();
+ }
+ mMutex.unlock_shared();
}
-Error AidlComposer::executeCommands() {
- return execute();
+Error AidlComposer::executeCommands(Display display) {
+ mMutex.lock_shared();
+ auto error = execute(display);
+ mMutex.unlock_shared();
+ return error;
}
uint32_t AidlComposer::getMaxVirtualDisplayCount() {
@@ -329,6 +343,7 @@
*outDisplay = translate<Display>(virtualDisplay.display);
*format = static_cast<PixelFormat>(virtualDisplay.format);
+ addDisplay(translate<Display>(virtualDisplay.display));
return Error::NONE;
}
@@ -338,12 +353,20 @@
ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
}
+ removeDisplay(display);
return Error::NONE;
}
Error AidlComposer::acceptDisplayChanges(Display display) {
- mWriter.acceptDisplayChanges(translate<int64_t>(display));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().acceptDisplayChanges(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::createLayer(Display display, Layer* outLayer) {
@@ -383,7 +406,17 @@
Error AidlComposer::getChangedCompositionTypes(
Display display, std::vector<Layer>* outLayers,
std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
- const auto changedLayers = mReader.takeChangedCompositionTypes(translate<int64_t>(display));
+ std::vector<ChangedCompositionLayer> changedLayers;
+ Error error = Error::NONE;
+ {
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ changedLayers = reader->get().takeChangedCompositionTypes(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ }
outLayers->reserve(changedLayers.size());
outTypes->reserve(changedLayers.size());
@@ -391,7 +424,7 @@
outLayers->emplace_back(translate<Layer>(layer.layer));
outTypes->emplace_back(layer.composition);
}
- return Error::NONE;
+ return error;
}
Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
@@ -443,7 +476,17 @@
Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
std::vector<Layer>* outLayers,
std::vector<uint32_t>* outLayerRequestMasks) {
- const auto displayRequests = mReader.takeDisplayRequests(translate<int64_t>(display));
+ Error error = Error::NONE;
+ DisplayRequest displayRequests;
+ {
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ displayRequests = reader->get().takeDisplayRequests(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ }
*outDisplayRequestMask = translate<uint32_t>(displayRequests.mask);
outLayers->reserve(displayRequests.layerRequests.size());
outLayerRequestMasks->reserve(displayRequests.layerRequests.size());
@@ -452,7 +495,7 @@
outLayers->emplace_back(translate<Layer>(layer.layer));
outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask));
}
- return Error::NONE;
+ return error;
}
Error AidlComposer::getDozeSupport(Display display, bool* outSupport) {
@@ -492,16 +535,35 @@
return static_cast<Error>(status.getServiceSpecificError());
}
- *outTypes = translate<Hdr>(capabilities.types);
+ *outTypes = capabilities.types;
*outMaxLuminance = capabilities.maxLuminance;
*outMaxAverageLuminance = capabilities.maxAverageLuminance;
*outMinLuminance = capabilities.minLuminance;
return Error::NONE;
}
+Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) {
+ const auto status = mAidlComposerClient->getOverlaySupport(outProperties);
+ if (!status.isOk()) {
+ ALOGE("getOverlaySupport failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) {
- auto fences = mReader.takeReleaseFences(translate<int64_t>(display));
+ Error error = Error::NONE;
+ std::vector<ReleaseFences::Layer> fences;
+ {
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ fences = reader->get().takeReleaseFences(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ }
outLayers->reserve(fences.size());
outReleaseFences->reserve(fences.size());
@@ -512,19 +574,29 @@
*fence.fence.getR() = -1;
outReleaseFences->emplace_back(fenceOwner);
}
- return Error::NONE;
+ return error;
}
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
ATRACE_NAME("HwcPresentDisplay");
- mWriter.presentDisplay(translate<int64_t>(display));
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ auto writer = getWriter(display);
+ auto reader = getReader(display);
+ if (writer && reader) {
+ writer->get().presentDisplay(translate<int64_t>(display));
+ error = execute(display);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
- Error error = execute();
if (error != Error::NONE) {
+ mMutex.unlock_shared();
return error;
}
- auto fence = mReader.takePresentFence(translate<int64_t>(display));
+ auto fence = reader->get().takePresentFence(translate<int64_t>(display));
+ mMutex.unlock_shared();
// take ownership
*outPresentFence = fence.get();
*fence.getR() = -1;
@@ -549,11 +621,19 @@
handle = target->getNativeBuffer()->handle;
}
- mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
- translate<aidl::android::hardware::graphics::common::Dataspace>(
- dataspace),
- translate<AidlRect>(damage));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get()
+ .setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
+ translate<aidl::android::hardware::graphics::common::Dataspace>(
+ dataspace),
+ translate<AidlRect>(damage));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
@@ -569,14 +649,28 @@
}
Error AidlComposer::setColorTransform(Display display, const float* matrix) {
- mWriter.setColorTransform(translate<int64_t>(display), matrix);
- return Error::NONE;
+ auto error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setColorTransform(translate<int64_t>(display), matrix);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
int releaseFence) {
- mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
- return Error::NONE;
+ auto error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
@@ -614,16 +708,26 @@
Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
uint32_t* outNumTypes, uint32_t* outNumRequests) {
ATRACE_NAME("HwcValidateDisplay");
- mWriter.validateDisplay(translate<int64_t>(display),
- ClockMonotonicTimestamp{expectedPresentTime});
+ const auto displayId = translate<int64_t>(display);
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ auto writer = getWriter(display);
+ auto reader = getReader(display);
+ if (writer && reader) {
+ writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime});
+ error = execute(display);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
- Error error = execute();
if (error != Error::NONE) {
+ mMutex.unlock_shared();
return error;
}
- mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+ reader->get().hasChanges(displayId, outNumTypes, outNumRequests);
+ mMutex.unlock_shared();
return Error::NONE;
}
@@ -631,39 +735,59 @@
uint32_t* outNumTypes, uint32_t* outNumRequests,
int* outPresentFence, uint32_t* state) {
ATRACE_NAME("HwcPresentOrValidateDisplay");
- mWriter.presentOrvalidateDisplay(translate<int64_t>(display),
- ClockMonotonicTimestamp{expectedPresentTime});
+ const auto displayId = translate<int64_t>(display);
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ auto writer = getWriter(display);
+ auto reader = getReader(display);
+ if (writer && reader) {
+ writer->get().presentOrvalidateDisplay(displayId,
+ ClockMonotonicTimestamp{expectedPresentTime});
+ error = execute(display);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
- Error error = execute();
if (error != Error::NONE) {
+ mMutex.unlock_shared();
return error;
}
- const auto result = mReader.takePresentOrValidateStage(translate<int64_t>(display));
+ const auto result = reader->get().takePresentOrValidateStage(displayId);
if (!result.has_value()) {
*state = translate<uint32_t>(-1);
+ mMutex.unlock_shared();
return Error::NO_RESOURCES;
}
*state = translate<uint32_t>(*result);
if (*result == PresentOrValidate::Result::Presented) {
- auto fence = mReader.takePresentFence(translate<int64_t>(display));
+ auto fence = reader->get().takePresentFence(displayId);
// take ownership
*outPresentFence = fence.get();
*fence.getR() = -1;
}
if (*result == PresentOrValidate::Result::Validated) {
- mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+ reader->get().hasChanges(displayId, outNumTypes, outNumRequests);
}
+ mMutex.unlock_shared();
return Error::NONE;
}
Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
- mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer),
+ x, y);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
@@ -673,90 +797,190 @@
handle = buffer->getNativeBuffer()->handle;
}
- mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle,
- acquireFence);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot,
+ handle, acquireFence);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) {
- mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlRect>(damage));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(damage));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerBlendMode(Display display, Layer layer,
IComposerClient::BlendMode mode) {
- mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
- translate<BlendMode>(mode));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<BlendMode>(mode));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) {
- mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerCompositionType(
Display display, Layer layer,
aidl::android::hardware::graphics::composer3::Composition type) {
- mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerCompositionType(translate<int64_t>(display),
+ translate<int64_t>(layer), type);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
- mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlDataspace>(dataspace));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlDataspace>(dataspace));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer,
const IComposerClient::Rect& frame) {
- mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlRect>(frame));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(frame));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
- mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer),
+ alpha);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerSidebandStream(Display display, Layer layer,
const native_handle_t* stream) {
- mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer),
+ stream);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerSourceCrop(Display display, Layer layer,
const IComposerClient::FRect& crop) {
- mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlFRect>(crop));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlFRect>(crop));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
- mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlTransform>(transform));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlTransform>(transform));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& visible) {
- mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlRect>(visible));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(visible));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
- mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
-Error AidlComposer::execute() {
- const auto& commands = mWriter.getPendingCommands();
+Error AidlComposer::execute(Display display) {
+ auto writer = getWriter(display);
+ auto reader = getReader(display);
+ if (!writer || !reader) {
+ return Error::BAD_DISPLAY;
+ }
+
+ const auto& commands = writer->get().getPendingCommands();
if (commands.empty()) {
- mWriter.reset();
+ writer->get().reset();
return Error::NONE;
}
@@ -768,9 +992,9 @@
return static_cast<Error>(status.getServiceSpecificError());
}
- mReader.parse(std::move(results));
+ reader->get().parse(std::move(results));
}
- const auto commandErrors = mReader.takeErrors();
+ const auto commandErrors = reader->get().takeErrors();
Error error = Error::NONE;
for (const auto& cmdErr : commandErrors) {
const auto index = static_cast<size_t>(cmdErr.commandIndex);
@@ -788,7 +1012,7 @@
}
}
- mWriter.reset();
+ writer->get().reset();
return error;
}
@@ -796,9 +1020,17 @@
Error AidlComposer::setLayerPerFrameMetadata(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
- mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlPerFrameMetadata>(perFrameMetadatas));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerPerFrameMetadata(translate<int64_t>(display),
+ translate<int64_t>(layer),
+ translate<AidlPerFrameMetadata>(perFrameMetadatas));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys(
@@ -858,8 +1090,16 @@
}
Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
- mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer),
+ matrix);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
@@ -922,20 +1162,36 @@
Error AidlComposer::setLayerPerFrameMetadataBlobs(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
- mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlPerFrameMetadataBlob>(metadata));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerPerFrameMetadataBlobs(translate<int64_t>(display),
+ translate<int64_t>(layer),
+ translate<AidlPerFrameMetadataBlob>(metadata));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setDisplayBrightness(Display display, float brightness, float brightnessNits,
const DisplayBrightnessOptions& options) {
- mWriter.setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits);
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits);
- if (options.applyImmediately) {
- return execute();
+ if (options.applyImmediately) {
+ error = execute(display);
+ mMutex.unlock_shared();
+ return error;
+ }
+ } else {
+ error = Error::BAD_DISPLAY;
}
-
- return Error::NONE;
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::getDisplayCapabilities(Display display,
@@ -1075,20 +1331,43 @@
Error AidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
- *outClientTargetProperty = mReader.takeClientTargetProperty(translate<int64_t>(display));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ *outClientTargetProperty =
+ reader->get().takeClientTargetProperty(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) {
- mWriter.setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer), brightness);
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer),
+ brightness);
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& blocking) {
- mWriter.setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
- translate<AidlRect>(blocking));
- return Error::NONE;
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ writer->get().setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(blocking));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
}
Error AidlComposer::getDisplayDecorationSupport(Display display,
@@ -1126,5 +1405,88 @@
return Error::NONE;
}
+ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
+ REQUIRES_SHARED(mMutex) {
+ return mWriters.get(display);
+}
+
+ftl::Optional<std::reference_wrapper<ComposerClientReader>> AidlComposer::getReader(Display display)
+ REQUIRES_SHARED(mMutex) {
+ if (mSingleReader) {
+ display = translate<Display>(kSingleReaderKey);
+ }
+ return mReaders.get(display);
+}
+
+void AidlComposer::removeDisplay(Display display) {
+ mMutex.lock();
+ bool wasErased = mWriters.erase(display);
+ ALOGW_IF(!wasErased,
+ "Attempting to remove writer for display %" PRId64 " which is not connected",
+ translate<int64_t>(display));
+ if (!mSingleReader) {
+ removeReader(display);
+ }
+ mMutex.unlock();
+}
+
+void AidlComposer::onHotplugDisconnect(Display display) {
+ removeDisplay(display);
+}
+
+bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
+ const auto displayId = translate<int64_t>(display);
+ std::vector<AidlDisplayCapability> capabilities;
+ const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
+ if (!status.isOk()) {
+ ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+ return false;
+ }
+ return std::find(capabilities.begin(), capabilities.end(),
+ AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end();
+}
+
+void AidlComposer::addReader(Display display) {
+ const auto displayId = translate<int64_t>(display);
+ std::optional<int64_t> displayOpt;
+ if (displayId != kSingleReaderKey) {
+ displayOpt.emplace(displayId);
+ }
+ auto [it, added] = mReaders.try_emplace(display, std::move(displayOpt));
+ ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected",
+ displayId);
+}
+
+void AidlComposer::removeReader(Display display) {
+ bool wasErased = mReaders.erase(display);
+ ALOGW_IF(!wasErased,
+ "Attempting to remove reader for display %" PRId64 " which is not connected",
+ translate<int64_t>(display));
+}
+
+void AidlComposer::addDisplay(Display display) {
+ const auto displayId = translate<int64_t>(display);
+ mMutex.lock();
+ auto [it, added] = mWriters.try_emplace(display, displayId);
+ ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected",
+ displayId);
+ if (mSingleReader) {
+ if (hasMultiThreadedPresentSupport(display)) {
+ mSingleReader = false;
+ removeReader(translate<Display>(kSingleReaderKey));
+ // Note that this includes the new display.
+ for (const auto& [existingDisplay, _] : mWriters) {
+ addReader(existingDisplay);
+ }
+ }
+ } else {
+ addReader(display);
+ }
+ mMutex.unlock();
+}
+
+void AidlComposer::onHotplugConnect(Display display) {
+ addDisplay(display);
+}
} // namespace Hwc2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 18d2242..2a043fd 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -17,10 +17,12 @@
#pragma once
#include "ComposerHal.h"
+#include <ftl/shared_mutex.h>
+#include <ftl/small_map.h>
+#include <functional>
#include <optional>
#include <string>
-#include <unordered_map>
#include <utility>
#include <vector>
@@ -48,6 +50,7 @@
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
using aidl::android::hardware::graphics::composer3::ComposerClientReader;
using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
class AidlIComposerCallbackWrapper;
@@ -69,10 +72,10 @@
// Reset all pending commands in the command buffer. Useful if you want to
// skip a frame but have already queued some commands.
- void resetCommands() override;
+ void resetCommands(Display) override;
// Explicitly flush all pending commands in the command buffer.
- Error executeCommands() override;
+ Error executeCommands(Display) override;
uint32_t getMaxVirtualDisplayCount() override;
Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
@@ -101,8 +104,9 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
+ Error getOverlaySupport(OverlayProperties* outProperties) override;
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
@@ -226,16 +230,29 @@
Error getPhysicalDisplayOrientation(Display displayId,
AidlTransform* outDisplayOrientation) override;
+ void onHotplugConnect(Display) override;
+ void onHotplugDisconnect(Display) override;
private:
// Many public functions above simply write a command into the command
// queue to batch the calls. validateDisplay and presentDisplay will call
// this function to execute the command queue.
- Error execute();
+ Error execute(Display) REQUIRES_SHARED(mMutex);
// returns the default instance name for the given service
static std::string instance(const std::string& serviceName);
+ ftl::Optional<std::reference_wrapper<ComposerClientWriter>> getWriter(Display)
+ REQUIRES_SHARED(mMutex);
+ ftl::Optional<std::reference_wrapper<ComposerClientReader>> getReader(Display)
+ REQUIRES_SHARED(mMutex);
+ void addDisplay(Display) EXCLUDES(mMutex);
+ void removeDisplay(Display) EXCLUDES(mMutex);
+ void addReader(Display) REQUIRES(mMutex);
+ void removeReader(Display) REQUIRES(mMutex);
+
+ bool hasMultiThreadedPresentSupport(Display);
+
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
// Max number of buffers that may be cached for a given layer
@@ -243,8 +260,25 @@
// 1. Tightly coupling this cache to the max size of BufferQueue
// 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- ComposerClientWriter mWriter;
- ComposerClientReader mReader;
+
+ // Without DisplayCapability::MULTI_THREADED_PRESENT, we use a single reader
+ // for all displays. With the capability, we use a separate reader for each
+ // display.
+ bool mSingleReader = true;
+ // Invalid displayId used as a key to mReaders when mSingleReader is true.
+ static constexpr int64_t kSingleReaderKey = 0;
+
+ // TODO (b/256881188): Use display::PhysicalDisplayMap instead of hard-coded `3`
+ ftl::SmallMap<Display, ComposerClientWriter, 3> mWriters GUARDED_BY(mMutex);
+ ftl::SmallMap<Display, ComposerClientReader, 3> mReaders GUARDED_BY(mMutex);
+ // Protect access to mWriters and mReaders with a shared_mutex. Adding and
+ // removing a display require exclusive access, since the iterator or the
+ // writer/reader may be invalidated. Other calls need shared access while
+ // using the writer/reader, so they can use their display's writer/reader
+ // without it being deleted or the iterator being invalidated.
+ // TODO (b/257958323): Use std::shared_mutex and RAII once they support
+ // threading annotations.
+ ftl::SharedMutex mMutex;
// Aidl interface
using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index d266d94..ec23935 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -38,6 +38,7 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <aidl/android/hardware/graphics/common/Transform.h>
#include <optional>
@@ -65,7 +66,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -83,6 +83,7 @@
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using aidl::android::hardware::graphics::common::Hdr;
class Composer {
public:
@@ -109,10 +110,10 @@
// Reset all pending commands in the command buffer. Useful if you want to
// skip a frame but have already queued some commands.
- virtual void resetCommands() = 0;
+ virtual void resetCommands(Display) = 0;
// Explicitly flush all pending commands in the command buffer.
- virtual Error executeCommands() = 0;
+ virtual Error executeCommands(Display) = 0;
virtual uint32_t getMaxVirtualDisplayCount() = 0;
virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*,
@@ -139,7 +140,7 @@
virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0;
- virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) = 0;
@@ -281,6 +282,9 @@
virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0;
virtual Error getPhysicalDisplayOrientation(Display displayId,
AidlTransform* outDisplayOrientation) = 0;
+ virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0;
+ virtual void onHotplugConnect(Display) = 0;
+ virtual void onHotplugDisconnect(Display) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index eb14933..ce602a8 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -72,6 +72,10 @@
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
@@ -88,31 +92,16 @@
}
status_t FramebufferSurface::advanceFrame() {
- uint32_t slot = 0;
- sp<GraphicBuffer> buf;
- sp<Fence> acquireFence(Fence::NO_FENCE);
- Dataspace dataspace = Dataspace::UNKNOWN;
- status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
- mDataSpace = dataspace;
- if (result != NO_ERROR) {
- ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
- strerror(-result), result);
- }
- return result;
-}
-
-status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
- sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
- Dataspace& outDataspace) {
Mutex::Autolock lock(mMutex);
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
+ mDataspace = Dataspace::UNKNOWN;
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
+ mDataspace = Dataspace::UNKNOWN;
return err;
}
@@ -133,13 +122,18 @@
mCurrentBufferSlot = item.mSlot;
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
mCurrentFence = item.mFence;
+ mDataspace = static_cast<Dataspace>(item.mDataSpace);
- outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
- outDataspace = static_cast<Dataspace>(item.mDataSpace);
- status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
+ // assume HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (mCurrentBuffer->getId() != mHwcBufferIds[mCurrentBufferSlot]) {
+ mHwcBufferIds[mCurrentBufferSlot] = mCurrentBuffer->getId();
+ hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
+ status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer,
+ mDataspace);
if (result != NO_ERROR) {
- ALOGE("error posting framebuffer: %d", result);
+ ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result);
return result;
}
@@ -190,7 +184,7 @@
limitedSize.width = maxSize.height * aspectRatio;
wasLimited = true;
}
- ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+ ALOGI_IF(wasLimited, "Framebuffer size has been limited to [%dx%d] from [%dx%d]",
limitedSize.width, limitedSize.height, size.width, size.height);
return limitedSize;
}
@@ -198,9 +192,9 @@
void FramebufferSurface::dumpAsString(String8& result) const {
Mutex::Autolock lock(mMutex);
result.append(" FramebufferSurface\n");
- result.appendFormat(" mDataSpace=%s (%d)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- mDataSpace);
+ result.appendFormat(" mDataspace=%s (%d)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataspace)).c_str(),
+ mDataspace);
ConsumerBase::dumpLocked(result, " ");
}
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index d41a856..0b863da 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <ui/DisplayId.h>
#include <ui/Size.h>
@@ -69,12 +69,6 @@
virtual void dumpLocked(String8& result, const char* prefix) const;
- // nextBuffer waits for and then latches the next buffer from the
- // BufferQueue and releases the previously latched buffer to the
- // BufferQueue. The new buffer is returned in the 'buffer' argument.
- status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
- sp<Fence>& outFence, ui::Dataspace& outDataspace);
-
const PhysicalDisplayId mDisplayId;
// Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
@@ -91,7 +85,7 @@
// compositing. Otherwise it will display the dataspace of the buffer
// use for compositing which can change as wide-color content is
// on/off.
- ui::Dataspace mDataSpace;
+ ui::Dataspace mDataspace;
// mCurrentBuffer is the current buffer or nullptr to indicate that there is
// no current buffer.
@@ -103,7 +97,9 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
+ // Buffers that HWC has seen before, indexed by slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 16d3984..d0126d0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -40,6 +40,7 @@
using aidl::android::hardware::graphics::composer3::Composition;
using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -319,20 +320,25 @@
float maxLuminance = -1.0f;
float maxAverageLuminance = -1.0f;
float minLuminance = -1.0f;
- std::vector<Hwc2::Hdr> types;
- auto intError = mComposer.getHdrCapabilities(mId, &types,
- &maxLuminance, &maxAverageLuminance, &minLuminance);
+ std::vector<Hwc2::Hdr> hdrTypes;
+ auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance,
+ &maxAverageLuminance, &minLuminance);
auto error = static_cast<HWC2::Error>(intError);
if (error != Error::NONE) {
return error;
}
- *outCapabilities = HdrCapabilities(std::move(types),
- maxLuminance, maxAverageLuminance, minLuminance);
+ *outCapabilities =
+ HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance);
return Error::NONE;
}
+Error Display::getOverlaySupport(OverlayProperties* outProperties) const {
+ auto intError = mComposer.getOverlaySupport(outProperties);
+ return static_cast<Error>(intError);
+}
+
Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
Dataspace* outDataspace,
uint8_t* outComponentMask) const {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 96399e2..4971d19 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -43,6 +43,7 @@
#include <aidl/android/hardware/graphics/composer3/Color.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -117,6 +118,9 @@
[[nodiscard]] virtual hal::Error supportsDoze(bool* outSupport) const = 0;
[[nodiscard]] virtual hal::Error getHdrCapabilities(
android::HdrCapabilities* outCapabilities) const = 0;
+ [[nodiscard]] virtual hal::Error getOverlaySupport(
+ aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties)
+ const = 0;
[[nodiscard]] virtual hal::Error getDisplayedContentSamplingAttributes(
hal::PixelFormat* outFormat, hal::Dataspace* outDataspace,
uint8_t* outComponentMask) const = 0;
@@ -204,6 +208,8 @@
hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+ hal::Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+ outProperties) const override;
hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
hal::Dataspace* outDataspace,
uint8_t* outComponentMask) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0a4ad97..10fde2a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -96,6 +96,7 @@
void HWComposer::setCallback(HWC2::ComposerCallback& callback) {
loadCapabilities();
loadLayerMetadataSupport();
+ loadOverlayProperties();
if (mRegisteredCallback) {
ALOGW("Callback already registered. Ignored extra registration attempt.");
@@ -513,7 +514,7 @@
if (displayData.validateWasSkipped) {
// explicitly flush all pending commands
- auto error = static_cast<hal::Error>(mComposer->executeCommands());
+ auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId()));
RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);
return NO_ERROR;
@@ -652,6 +653,11 @@
return NO_ERROR;
}
+const aidl::android::hardware::graphics::composer3::OverlayProperties&
+HWComposer::getOverlaySupport() const {
+ return mOverlayProperties;
+}
+
int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
@@ -928,6 +934,8 @@
: "Secondary display",
.deviceProductInfo = std::nullopt};
}();
+
+ mComposer->onHotplugConnect(hwcDisplayId);
}
if (!isConnected(info->id)) {
@@ -955,6 +963,7 @@
// The display will later be destroyed by a call to HWComposer::disconnectDisplay. For now, mark
// it as disconnected.
mDisplayData.at(*displayId).hwcDisplay->setConnected(false);
+ mComposer->onHotplugDisconnect(hwcDisplayId);
return DisplayIdentificationInfo{.id = *displayId};
}
@@ -966,6 +975,10 @@
}
}
+void HWComposer::loadOverlayProperties() {
+ mComposer->getOverlaySupport(&mOverlayProperties);
+}
+
status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId,
std::chrono::milliseconds timeout) {
ATRACE_CALL();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 6c43d8b..78d4a68 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -48,6 +48,7 @@
#include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -178,6 +179,9 @@
// Fetches the HDR capabilities of the given display
virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
+ virtual const aidl::android::hardware::graphics::composer3::OverlayProperties&
+ getOverlaySupport() const = 0;
+
virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
// Returns the available RenderIntent of the given display.
@@ -362,6 +366,9 @@
// Fetches the HDR capabilities of the given display
status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
+ const aidl::android::hardware::graphics::composer3::OverlayProperties& getOverlaySupport()
+ const override;
+
int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
// Returns the available RenderIntent of the given display.
@@ -482,11 +489,13 @@
void loadCapabilities();
void loadLayerMetadataSupport();
+ void loadOverlayProperties();
std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities;
+ aidl::android::hardware::graphics::composer3::OverlayProperties mOverlayProperties;
std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
bool mRegisteredCallback = false;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 4737034..537d545 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/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
@@ -39,7 +40,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Error;
@@ -69,6 +69,7 @@
using PowerMode = IComposerClient::PowerMode;
using Vsync = IComposerClient::Vsync;
using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+using Hdr = aidl::android::hardware::graphics::common::Hdr;
} // namespace hardware::graphics::composer::hal
@@ -177,6 +178,10 @@
return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
}
+// For utils::Dumper ADL.
+namespace hardware::graphics::composer {
+namespace V2_2 {
+
inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) {
switch (mode) {
case hardware::graphics::composer::hal::PowerMode::OFF:
@@ -194,6 +199,10 @@
}
}
+} // namespace V2_2
+
+namespace V2_1 {
+
inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) {
switch (vsync) {
case hardware::graphics::composer::hal::Vsync::ENABLE:
@@ -205,4 +214,6 @@
}
}
+} // namespace V2_1
+} // namespace hardware::graphics::composer
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 2597ae6..b607df0 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -40,6 +40,7 @@
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using aidl::android::hardware::graphics::composer3::DimmingStage;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -272,11 +273,11 @@
}
}
-void HidlComposer::resetCommands() {
+void HidlComposer::resetCommands(Display) {
mWriter.reset();
}
-Error HidlComposer::executeCommands() {
+Error HidlComposer::executeCommands(Display) {
return execute();
}
@@ -495,13 +496,13 @@
"OptionalFeature::KernelIdleTimer is not supported on HIDL");
}
-Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) {
Error error = kDefaultError;
if (mClient_2_3) {
mClient_2_3->getHdrCapabilities_2_3(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -509,15 +510,15 @@
if (error != Error::NONE) {
return;
}
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
- *outTypes = tmpTypes;
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
*outMinLuminance = tmpMinLuminance;
});
} else {
mClient->getHdrCapabilities(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -525,11 +526,7 @@
if (error != Error::NONE) {
return;
}
-
- outTypes->clear();
- for (auto type : tmpTypes) {
- outTypes->push_back(static_cast<Hdr>(type));
- }
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
@@ -540,6 +537,10 @@
return error;
}
+Error HidlComposer::getOverlaySupport(OverlayProperties* /*outProperties*/) {
+ return Error::NONE;
+}
+
Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) {
mReader.takeReleaseFences(display, outLayers, outReleaseFences);
@@ -1352,6 +1353,9 @@
registerCallback(sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported));
}
+void HidlComposer::onHotplugConnect(Display) {}
+void HidlComposer::onHotplugDisconnect(Display) {}
+
CommandReader::~CommandReader() {
resetData();
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d0d3c2e..3602bbb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -56,7 +56,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -177,10 +176,10 @@
// Reset all pending commands in the command buffer. Useful if you want to
// skip a frame but have already queued some commands.
- void resetCommands() override;
+ void resetCommands(Display) override;
// Explicitly flush all pending commands in the command buffer.
- Error executeCommands() override;
+ Error executeCommands(Display) override;
uint32_t getMaxVirtualDisplayCount() override;
Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
@@ -209,8 +208,10 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
+ Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+ outProperties) override;
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
@@ -337,6 +338,8 @@
Error getPhysicalDisplayOrientation(Display displayId,
AidlTransform* outDisplayOrientation) override;
+ void onHotplugConnect(Display) override;
+ void onHotplugDisconnect(Display) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index cb2c8c5..f05223c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -57,6 +57,7 @@
using android::hardware::power::IPower;
using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
+using android::hardware::power::SessionHint;
using android::hardware::power::WorkDuration;
PowerAdvisor::~PowerAdvisor() = default;
@@ -140,7 +141,7 @@
}
}
-void PowerAdvisor::notifyDisplayUpdateImminent() {
+void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
// Only start sending this notification once the system has booted so we don't introduce an
// early-boot dependency on Power HAL
if (!mBootFinished.load()) {
@@ -154,7 +155,7 @@
return;
}
- if (!halWrapper->notifyDisplayUpdateImminent()) {
+ if (!halWrapper->notifyDisplayUpdateImminentAndCpuReset()) {
// The HAL has become unavailable; attempt to reconnect later
mReconnectPowerHal = true;
return;
@@ -599,7 +600,7 @@
return ret.isOk();
}
- bool notifyDisplayUpdateImminent() override {
+ bool notifyDisplayUpdateImminentAndCpuReset() override {
// Power HAL 1.x doesn't have a notification for this
ALOGV("HIDL notifyUpdateImminent received but can't send");
return true;
@@ -675,8 +676,12 @@
return ret.isOk();
}
-bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
- ALOGV("AIDL notifyDisplayUpdateImminent");
+bool AidlPowerHalWrapper::notifyDisplayUpdateImminentAndCpuReset() {
+ ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
+ if (isPowerHintSessionRunning()) {
+ mPowerHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+ }
+
if (!mHasDisplayUpdateImminent) {
ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
return true;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 1c9d123..d45e7cb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -48,7 +48,7 @@
virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
- virtual void notifyDisplayUpdateImminent() = 0;
+ virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
// Checks both if it supports and if it's enabled
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
@@ -106,7 +106,7 @@
virtual ~HalWrapper() = default;
virtual bool setExpensiveRendering(bool enabled) = 0;
- virtual bool notifyDisplayUpdateImminent() = 0;
+ virtual bool notifyDisplayUpdateImminentAndCpuReset() = 0;
virtual bool supportsPowerHintSession() = 0;
virtual bool isPowerHintSessionRunning() = 0;
virtual void restartPowerHintSession() = 0;
@@ -126,7 +126,7 @@
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
- void notifyDisplayUpdateImminent() override;
+ void notifyDisplayUpdateImminentAndCpuReset() override;
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
bool isPowerHintSessionRunning() override;
@@ -289,7 +289,7 @@
static std::unique_ptr<HalWrapper> connect();
bool setExpensiveRendering(bool enabled) override;
- bool notifyDisplayUpdateImminent() override;
+ bool notifyDisplayUpdateImminentAndCpuReset() override;
bool supportsPowerHintSession() override;
bool isPowerHintSessionRunning() override;
void restartPowerHintSession() override;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 3803a78..d62075e 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -103,6 +103,10 @@
sink->setAsyncMode(true);
IGraphicBufferProducer::QueueBufferOutput output;
mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
VirtualDisplaySurface::~VirtualDisplaySurface() {
@@ -197,9 +201,9 @@
return NO_MEMORY;
}
- sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
- mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
- sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
+ sp<GraphicBuffer> const& fbBuffer =
+ mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
+ sp<GraphicBuffer> const& outBuffer = mProducerBuffers[mOutputProducerSlot];
VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
@@ -211,12 +215,14 @@
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
-
+ // assume that HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) {
+ mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId();
+ hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN);
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index e21095a..be06e2b 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,7 +20,7 @@
#include <string>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
#include <ui/DisplayId.h>
@@ -164,6 +164,10 @@
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
+ // Buffers that HWC has seen before, indexed by HWC slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
+
//
// Inter-frame state
//
@@ -260,8 +264,6 @@
bool mMustRecompose = false;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
-
bool mForceHwcCopy;
};
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c73a75c..cd1ba70 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -892,6 +892,10 @@
mJankType = JankType::Unknown;
deadlineDelta = 0;
deltaToVsync = 0;
+ if (mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+ mSurfaceFlingerActuals.presentTime = mSurfaceFlingerActuals.endTime;
+ }
+
return;
}
@@ -1168,22 +1172,50 @@
static_cast<float>(totalPresentToPresentWalls);
}
+std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const {
+ for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
+ const auto& [fence, _] = mPendingPresentFences[i];
+ if (fence && fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ return i;
+ }
+ }
+
+ return {};
+}
+
void FrameTimeline::flushPendingPresentFences() {
+ const auto firstSignaledFence = getFirstSignalFenceIndex();
+ if (!firstSignaledFence.has_value()) {
+ return;
+ }
+
// Perfetto is using boottime clock to void drifts when the device goes
// to suspend.
const auto monoBootOffset = mUseBootTimeClock
? (systemTime(SYSTEM_TIME_BOOTTIME) - systemTime(SYSTEM_TIME_MONOTONIC))
: 0;
+ // Present fences are expected to be signaled in order. Mark all the previous
+ // pending fences as errors.
+ for (size_t i = 0; i < firstSignaledFence.value(); i++) {
+ 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);
+ mPendingPresentFences.erase(mPendingPresentFences.begin());
+ }
+
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
signalTime = pendingPresentFence.first->getSignalTime();
if (signalTime == Fence::SIGNAL_TIME_PENDING) {
- continue;
+ break;
}
}
+
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousPresentTime);
displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a2305af..31074b1 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -474,6 +474,7 @@
friend class android::frametimeline::FrameTimelineTest;
void flushPendingPresentFences() REQUIRES(mMutex);
+ std::optional<size_t> getFirstSignalFenceIndex() const REQUIRES(mMutex);
void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
void dumpAll(std::string& result);
void dumpJank(std::string& result);
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
new file mode 100644
index 0000000..0c7b24a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -0,0 +1,34 @@
+/*
+ * 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 <gui/DisplayInfo.h>
+
+namespace android::surfaceflinger::frontend {
+
+// Display information needed to populate input and calculate layer geometry.
+struct DisplayInfo {
+ gui::DisplayInfo info;
+ ui::Transform transform;
+ bool receivesInput;
+ bool isSecure;
+ // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
+ bool isPrimary;
+ ui::Transform::RotationFlags rotationFlags;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
new file mode 100644
index 0000000..6d492c0
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "LayerCreationArgs.h"
+#include <binder/IPCThreadState.h>
+#include <private/android_filesystem_config.h>
+#include "Client.h"
+#include "gui/LayerMetadata.h"
+
+namespace android::surfaceflinger {
+
+std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+ uint32_t flags, gui::LayerMetadata metadataArg,
+ std::optional<uint32_t> id)
+ : flinger(flinger),
+ client(std::move(client)),
+ name(std::move(name)),
+ flags(flags),
+ metadata(std::move(metadataArg)) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ ownerPid = ipc->getCallingPid();
+ uid_t callingUid = ipc->getCallingUid();
+ metadata.setInt32(gui::METADATA_CALLING_UID, static_cast<int32_t>(callingUid));
+ ownerUid = callingUid;
+ if (ownerUid == AID_GRAPHICS || ownerUid == AID_SYSTEM) {
+ // System can override the calling UID/PID since it can create layers on behalf of apps.
+ ownerPid = metadata.getInt32(gui::METADATA_OWNER_PID, ownerPid);
+ ownerUid = static_cast<uid_t>(
+ metadata.getInt32(gui::METADATA_OWNER_UID, static_cast<int32_t>(ownerUid)));
+ }
+
+ if (id) {
+ sequence = *id;
+ sSequence = *id + 1;
+ } else {
+ sequence = sSequence++;
+ if (sequence == UNASSIGNED_LAYER_ID) {
+ ALOGW("Layer sequence id rolled over.");
+ sequence = sSequence++;
+ }
+ }
+}
+
+LayerCreationArgs::LayerCreationArgs(const LayerCreationArgs& args)
+ : LayerCreationArgs(args.flinger, args.client, args.name, args.flags, args.metadata) {}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
new file mode 100644
index 0000000..7b5a157
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <binder/Binder.h>
+#include <gui/LayerMetadata.h>
+#include <utils/StrongPointer.h>
+#include <cstdint>
+#include <limits>
+#include <optional>
+constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
+
+namespace android {
+class SurfaceFlinger;
+class Client;
+} // namespace android
+
+namespace android::surfaceflinger {
+
+struct LayerCreationArgs {
+ static std::atomic<uint32_t> sSequence;
+
+ LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name,
+ uint32_t flags, gui::LayerMetadata,
+ std::optional<uint32_t> id = std::nullopt);
+ LayerCreationArgs(const LayerCreationArgs&);
+
+ android::SurfaceFlinger* flinger;
+ sp<android::Client> client;
+ std::string name;
+ uint32_t flags; // ISurfaceComposerClient flags
+ gui::LayerMetadata metadata;
+ pid_t ownerPid;
+ uid_t ownerUid;
+ uint32_t textureName;
+ uint32_t sequence;
+ bool addToRoot = true;
+ wp<IBinder> parentHandle = nullptr;
+ wp<IBinder> mirrorLayerHandle = nullptr;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
new file mode 100644
index 0000000..75e4e3a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "LayerHandle.h"
+#include <cstdint>
+#include "Layer.h"
+#include "LayerCreationArgs.h"
+#include "SurfaceFlinger.h"
+
+namespace android::surfaceflinger {
+
+LayerHandle::LayerHandle(const sp<android::SurfaceFlinger>& flinger,
+ const sp<android::Layer>& layer)
+ : mFlinger(flinger), mLayer(layer), mLayerId(static_cast<uint32_t>(layer->getSequence())) {}
+
+LayerHandle::~LayerHandle() {
+ if (mFlinger) {
+ mFlinger->onHandleDestroyed(this, mLayer, mLayerId);
+ }
+}
+
+const String16 LayerHandle::kDescriptor = String16("android.Layer.LayerHandle");
+
+sp<LayerHandle> LayerHandle::fromIBinder(const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ return nullptr;
+ }
+
+ BBinder* b = binder->localBinder();
+ if (b == nullptr || b->getInterfaceDescriptor() != LayerHandle::kDescriptor) {
+ ALOGD("handle does not have a valid descriptor");
+ return nullptr;
+ }
+
+ // We can safely cast this binder since its local and we verified its interface descriptor.
+ return sp<LayerHandle>::cast(binder);
+}
+
+sp<android::Layer> LayerHandle::getLayer(const sp<IBinder>& binder) {
+ sp<LayerHandle> handle = LayerHandle::fromIBinder(binder);
+ return handle ? handle->mLayer : nullptr;
+}
+
+uint32_t LayerHandle::getLayerId(const sp<IBinder>& binder) {
+ sp<LayerHandle> handle = LayerHandle::fromIBinder(binder);
+ return handle ? handle->mLayerId : UNASSIGNED_LAYER_ID;
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.h b/services/surfaceflinger/FrontEnd/LayerHandle.h
new file mode 100644
index 0000000..5d0f783
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.h
@@ -0,0 +1,58 @@
+/*
+ * 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 <binder/Binder.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+class SurfaceFlinger;
+class Layer;
+} // namespace android
+
+namespace android::surfaceflinger {
+
+/*
+ * The layer handle is just a BBinder object passed to the client
+ * (remote process) -- we don't keep any reference on our side such that
+ * the dtor is called when the remote side let go of its reference.
+ *
+ * ~LayerHandle ensures that mFlinger->onLayerDestroyed() is called for
+ * this layer when the handle is destroyed.
+ */
+class LayerHandle : public BBinder {
+public:
+ LayerHandle(const sp<android::SurfaceFlinger>& flinger, const sp<android::Layer>& layer);
+ // for testing
+ LayerHandle(uint32_t layerId) : mFlinger(nullptr), mLayer(nullptr), mLayerId(layerId) {}
+ ~LayerHandle();
+
+ // Static functions to access the layer and layer id safely from an incoming binder.
+ static sp<LayerHandle> fromIBinder(const sp<IBinder>& handle);
+ static sp<android::Layer> getLayer(const sp<IBinder>& handle);
+ static uint32_t getLayerId(const sp<IBinder>& handle);
+ static const String16 kDescriptor;
+
+ const String16& getInterfaceDescriptor() const override { return kDescriptor; }
+
+private:
+ sp<android::SurfaceFlinger> mFlinger;
+ sp<android::Layer> mLayer;
+ const uint32_t mLayerId;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
new file mode 100644
index 0000000..db4e8af
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerHierarchy"
+
+#include "LayerHierarchy.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs,
+ const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
+ auto lhsLayer = lhs.first->getLayer();
+ auto rhsLayer = rhs.first->getLayer();
+ if (lhsLayer->layerStack != rhsLayer->layerStack) {
+ return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+ }
+ if (lhsLayer->z != rhsLayer->z) {
+ return lhsLayer->z < rhsLayer->z;
+ }
+ return lhsLayer->id < rhsLayer->id;
+};
+
+void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec,
+ std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) {
+ auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare);
+ vec.insert(it, std::move(value));
+}
+} // namespace
+
+LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {}
+
+LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) {
+ mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer;
+ mChildren = hierarchy.mChildren;
+}
+
+void LayerHierarchy::traverse(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ if (mLayer) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ }
+ if (traversalPath.hasRelZLoop()) {
+ LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
+ }
+ for (auto& [child, childVariant] : mChildren) {
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverse(visitor, traversalPath);
+ }
+}
+
+void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ bool traverseThisLayer = (mLayer != nullptr);
+ for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
+ auto& [child, childVariant] = *it;
+ if (traverseThisLayer && child->getLayer()->z >= 0) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ traverseThisLayer = false;
+ }
+ if (childVariant == LayerHierarchy::Variant::Detached) {
+ continue;
+ }
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverseInZOrder(visitor, traversalPath);
+ }
+
+ if (traverseThisLayer) {
+ visitor(*this, traversalPath);
+ }
+}
+
+void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) {
+ insertSorted(mChildren, {child, variant});
+}
+
+void LayerHierarchy::removeChild(LayerHierarchy* child) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [child](const std::pair<LayerHierarchy*, Variant>& x) {
+ return x.first == child;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ }
+ mChildren.erase(it);
+}
+
+void LayerHierarchy::sortChildrenByZOrder() {
+ std::sort(mChildren.begin(), mChildren.end(), layerZCompare);
+}
+
+void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
+ return child.first == hierarchy;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ } else {
+ it->second = variant;
+ }
+}
+
+const RequestedLayerState* LayerHierarchy::getLayer() const {
+ return mLayer;
+}
+
+std::string LayerHierarchy::getDebugStringShort() const {
+ std::string debug = "LayerHierarchy{";
+ debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " ";
+ if (mChildren.empty()) {
+ debug += "no children";
+ } else {
+ debug += std::to_string(mChildren.size()) + " children";
+ }
+ return debug + "}";
+}
+
+std::string LayerHierarchy::getDebugString(const char* prefix) const {
+ std::string debug = prefix + getDebugStringShort();
+ for (auto& [child, childVariant] : mChildren) {
+ std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant);
+ debug += "\n" + child->getDebugString(childPrefix.c_str());
+ }
+ return debug;
+}
+
+bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
+ outInvalidRelativeRoot = UNASSIGNED_LAYER_ID;
+ traverse([&outInvalidRelativeRoot](const LayerHierarchy&,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ if (traversalPath.hasRelZLoop()) {
+ outInvalidRelativeRoot = traversalPath.invalidRelativeRootId;
+ return false;
+ }
+ return true;
+ });
+ return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
+}
+
+LayerHierarchyBuilder::LayerHierarchyBuilder(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+ mHierarchies.reserve(layers.size());
+ mLayerIdToHierarchy.reserve(layers.size());
+ for (auto& layer : layers) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ for (const auto& layer : layers) {
+ onLayerAdded(layer.get());
+ }
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+}
+
+void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ LayerHierarchy::Variant type = layer->hasValidRelativeParent()
+ ? LayerHierarchy::Variant::Detached
+ : LayerHierarchy::Variant::Attached;
+
+ LayerHierarchy* parent;
+
+ if (layer->parentId != UNASSIGNED_LAYER_ID) {
+ parent = getHierarchyFromId(layer->parentId);
+ } else if (layer->canBeRoot) {
+ parent = &mRoot;
+ } else {
+ parent = &mOffscreenRoot;
+ }
+ parent->addChild(hierarchy, type);
+ hierarchy->mParent = parent;
+}
+
+void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) {
+ hierarchy->mParent->removeChild(hierarchy);
+ hierarchy->mParent = nullptr;
+}
+
+void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) {
+ return;
+ }
+
+ if (layer->relativeParentId != UNASSIGNED_LAYER_ID) {
+ hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId);
+ } else {
+ hierarchy->mRelativeParent = &mOffscreenRoot;
+ }
+ hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative);
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached);
+}
+
+void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) {
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->removeChild(hierarchy);
+ }
+ hierarchy->mRelativeParent = nullptr;
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ attachToRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ attachHierarchyToRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ detachFromRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ detachHierarchyFromRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ attachToParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+ hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
+ if (!hierarchy) {
+ // Layer was never part of the hierarchy if it was created and destroyed in the same
+ // transaction.
+ return;
+ }
+ // detach from parent
+ detachFromRelativeParent(hierarchy);
+ detachFromParent(hierarchy);
+
+ // detach children
+ for (auto& [child, variant] : hierarchy->mChildren) {
+ if (variant == LayerHierarchy::Variant::Attached ||
+ variant == LayerHierarchy::Variant::Detached) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mParent = &mOffscreenRoot;
+ } else if (variant == LayerHierarchy::Variant::Relative) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mRelativeParent = &mOffscreenRoot;
+ }
+ }
+
+ swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) {
+ return layerHierarchy.get() == hierarchy;
+ });
+ mLayerIdToHierarchy.erase(layer->id);
+}
+
+void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ auto it = hierarchy->mChildren.begin();
+ while (it != hierarchy->mChildren.end()) {
+ if (it->second == LayerHierarchy::Variant::Mirror) {
+ hierarchy->mChildren.erase(it);
+ break;
+ }
+ it++;
+ }
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::update(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
+ // rebuild map
+ for (auto& layer : layers) {
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ }
+
+ for (auto& layer : layers) {
+ if (layer->changes.get() == 0) {
+ continue;
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ onLayerAdded(layer.get());
+ continue;
+ }
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ if (layer->changes.test(RequestedLayerState::Changes::Parent)) {
+ detachFromParent(hierarchy);
+ attachToParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) {
+ detachFromRelativeParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Z)) {
+ hierarchy->mParent->sortChildrenByZOrder();
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->sortChildrenByZOrder();
+ }
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Mirror)) {
+ updateMirrorLayer(layer.get());
+ }
+ }
+
+ for (auto& layer : destroyedLayers) {
+ onLayerDestroyed(layer.get());
+ }
+ // When moving from onscreen to offscreen and vice versa, we need to attach and detach
+ // from our relative parents. This walks down both trees to do so. We can optimize this
+ // further by tracking onscreen, offscreen state in LayerHierarchy.
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+ attachHierarchyToRelativeParent(&mRoot);
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
+ return mRoot;
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const {
+ return mOffscreenRoot;
+}
+
+std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const {
+ if (depth > 10) return "too deep, loop?";
+ if (layerId == UNASSIGNED_LAYER_ID) return "";
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return "not found";
+
+ LayerHierarchy* hierarchy = it->second;
+ if (!hierarchy->mLayer) return "none";
+
+ std::string debug =
+ "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name;
+ if (hierarchy->mRelativeParent) {
+ debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort();
+ }
+ if (hierarchy->mParent) {
+ debug += " Parent:" + hierarchy->mParent->getDebugStringShort();
+ }
+ return debug;
+}
+
+LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId,
+ bool childrenOnly) const {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return {nullptr};
+
+ LayerHierarchy hierarchy(*it->second, childrenOnly);
+ return hierarchy;
+}
+
+LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) {
+ if (crashOnFailure) {
+ LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
+ }
+ return nullptr;
+ };
+
+ return it->second;
+}
+
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID =
+ {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
+
+std::string LayerHierarchy::TraversalPath::toString() const {
+ std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+
+ if (!mirrorRootIds.empty()) {
+ debugString += ", .mirrorRootIds=";
+ for (auto rootId : mirrorRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (!relativeRootIds.empty()) {
+ debugString += ", .relativeRootIds=";
+ for (auto rootId : relativeRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (hasRelZLoop()) {
+ debugString += ", hasRelZLoop=true invalidRelativeRootId=";
+ debugString += std::to_string(invalidRelativeRootId) + ",";
+ }
+
+ debugString += "}";
+ return debugString;
+}
+
+// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
+// of scope the TraversalPath is reset to its original state.
+LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
+ uint32_t layerId,
+ LayerHierarchy::Variant variant)
+ : mTraversalPath(traversalPath),
+ mParentId(traversalPath.id),
+ mParentVariant(traversalPath.variant) {
+ // Update the traversal id with the child layer id and variant. Parent id and variant are
+ // stored to reset the id upon destruction.
+ traversalPath.id = layerId;
+ traversalPath.variant = variant;
+ if (variant == LayerHierarchy::Variant::Mirror) {
+ traversalPath.mirrorRootIds.emplace_back(layerId);
+ }
+ if (variant == LayerHierarchy::Variant::Relative) {
+ if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
+ layerId) != traversalPath.relativeRootIds.end()) {
+ traversalPath.invalidRelativeRootId = layerId;
+ }
+ traversalPath.relativeRootIds.emplace_back(layerId);
+ }
+}
+LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
+ // Reset the traversal id to its original parent state using the state that was saved in
+ // the constructor.
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+ mTraversalPath.mirrorRootIds.pop_back();
+ }
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
+ mTraversalPath.relativeRootIds.pop_back();
+ }
+ if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
+ mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ }
+ mTraversalPath.id = mParentId;
+ mTraversalPath.variant = mParentVariant;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
new file mode 100644
index 0000000..f83a859
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -0,0 +1,163 @@
+/*
+ * 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 "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "ftl/small_vector.h"
+
+namespace android::surfaceflinger::frontend {
+class LayerHierarchyBuilder;
+
+// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
+// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
+// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
+//
+// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
+// traversing the hierarchy, a new concept called Variant can be used to understand the
+// relationship of the layer to its parent. The following variants are possible:
+// Attached - child of the parent
+// Detached - child of the parent but currently relative parented to another layer
+// Relative - relative child of the parent
+// Mirror - mirrored from another layer
+//
+// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
+// cloning the layer requested state. The mirrored hierarchy and its corresponding
+// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
+// states.
+class LayerHierarchy {
+public:
+ enum Variant {
+ Attached,
+ Detached,
+ Relative,
+ Mirror,
+ };
+ // Represents a unique path to a node.
+ struct TraversalPath {
+ uint32_t id;
+ LayerHierarchy::Variant variant;
+ // Mirrored layers can have a different geometry than their parents so we need to track
+ // the mirror roots in the traversal.
+ ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+ // Relative layers can be visited twice, once by their parent and then once again by
+ // their relative parent. We keep track of the roots here to detect any loops in the
+ // hierarchy. If a relative root already exists in the list while building the
+ // TraversalPath, it means that somewhere in the hierarchy two layers are relatively
+ // parented to each other.
+ ftl::SmallVector<uint32_t, 5> relativeRootIds;
+ // First duplicate relative root id found. If this is a valid layer id that means we are
+ // in a loop.
+ uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; }
+ bool isRelative() { return !relativeRootIds.empty(); }
+
+ bool operator==(const TraversalPath& other) const {
+ return id == other.id && mirrorRootIds == other.mirrorRootIds;
+ }
+ std::string toString() const;
+
+ static TraversalPath ROOT_TRAVERSAL_ID;
+ };
+
+ // Helper class to add nodes to an existing traversal id and removes the
+ // node when it goes out of scope.
+ class ScopedAddToTraversalPath {
+ public:
+ ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
+ LayerHierarchy::Variant variantArg);
+ ~ScopedAddToTraversalPath();
+
+ private:
+ TraversalPath& mTraversalPath;
+ uint32_t mParentId;
+ LayerHierarchy::Variant mParentVariant;
+ };
+ LayerHierarchy(RequestedLayerState* layer);
+
+ // Visitor function that provides the hierarchy node and a traversal id which uniquely
+ // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
+ // Return false to stop traversing down the hierarchy.
+ typedef std::function<bool(const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath)>
+ Visitor;
+
+ // Traverse the hierarchy and visit all child variants.
+ void traverse(const Visitor& visitor) const {
+ traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ // Traverse the hierarchy in z-order, skipping children that have relative parents.
+ void traverseInZOrder(const Visitor& visitor) const {
+ traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ const RequestedLayerState* getLayer() const;
+ std::string getDebugString(const char* prefix = "") const;
+ std::string getDebugStringShort() const;
+ // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
+ // will contain the first relative root that was visited twice in a traversal.
+ bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
+ std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
+
+private:
+ friend LayerHierarchyBuilder;
+ LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
+ void addChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void removeChild(LayerHierarchy*);
+ void sortChildrenByZOrder();
+ void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+
+ const RequestedLayerState* mLayer;
+ LayerHierarchy* mParent = nullptr;
+ LayerHierarchy* mRelativeParent = nullptr;
+};
+
+// Given a list of RequestedLayerState, this class will build a root hierarchy and an
+// offscreen hierarchy. The builder also has an update method which can update an existing
+// 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);
+ LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
+ const LayerHierarchy& getHierarchy() const;
+ const LayerHierarchy& getOffscreenHierarchy() const;
+ std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+
+private:
+ void onLayerAdded(RequestedLayerState* layer);
+ void attachToParent(LayerHierarchy*);
+ void detachFromParent(LayerHierarchy*);
+ void attachToRelativeParent(LayerHierarchy*);
+ void detachFromRelativeParent(LayerHierarchy*);
+ void attachHierarchyToRelativeParent(LayerHierarchy*);
+ void detachHierarchyFromRelativeParent(LayerHierarchy*);
+
+ 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};
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
new file mode 100644
index 0000000..fdf60b3
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "LayerLifecycleManager"
+
+#include "LayerLifecycleManager.h"
+#include "Layer.h" // temporarily needed for LayerHandle
+#include "LayerHandle.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+using namespace ftl::flag_operators;
+
+void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) {
+ if (newLayers.empty()) {
+ return;
+ }
+
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+ for (auto& newLayer : newLayers) {
+ RequestedLayerState& layer = *newLayer.get();
+ auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
+ if (!inserted) {
+ LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
+ it->second.owner.getDebugString().c_str());
+ }
+
+ layer.parentId = linkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
+
+ mLayers.emplace_back(std::move(newLayer));
+ }
+}
+
+void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles) {
+ std::vector<uint32_t> layersToBeDestroyed;
+ for (const auto& layerId : destroyedHandles) {
+ auto it = mIdToLayer.find(layerId);
+ if (it == mIdToLayer.end()) {
+ LOG_ALWAYS_FATAL("%s Layerid not found %d", __func__, layerId);
+ continue;
+ }
+ RequestedLayerState& layer = it->second.owner;
+ layer.handleAlive = false;
+ if (!layer.canBeDestroyed()) {
+ continue;
+ }
+ layer.changes |= RequestedLayerState::Changes::Destroyed;
+ layersToBeDestroyed.emplace_back(layerId);
+ }
+
+ if (layersToBeDestroyed.empty()) {
+ return;
+ }
+
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+ for (size_t i = 0; i < layersToBeDestroyed.size(); i++) {
+ uint32_t layerId = layersToBeDestroyed[i];
+ auto it = mIdToLayer.find(layerId);
+ if (it == mIdToLayer.end()) {
+ LOG_ALWAYS_FATAL("%s Layer with id %d not found", __func__, layerId);
+ continue;
+ }
+
+ RequestedLayerState& layer = it->second.owner;
+
+ layer.parentId = unlinkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
+
+ auto& references = it->second.references;
+ for (uint32_t linkedLayerId : references) {
+ RequestedLayerState* linkedLayer = getLayerFromId(linkedLayerId);
+ if (!linkedLayer) {
+ LOG_ALWAYS_FATAL("%s Layerid reference %d not found for %d", __func__,
+ linkedLayerId, layer.id);
+ continue;
+ };
+ if (linkedLayer->parentId == layer.id) {
+ linkedLayer->parentId = UNASSIGNED_LAYER_ID;
+ if (linkedLayer->canBeDestroyed()) {
+ linkedLayer->changes |= RequestedLayerState::Changes::Destroyed;
+ layersToBeDestroyed.emplace_back(linkedLayer->id);
+ }
+ }
+ if (linkedLayer->relativeParentId == layer.id) {
+ linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID;
+ }
+ if (linkedLayer->mirrorId == layer.id) {
+ linkedLayer->mirrorId = UNASSIGNED_LAYER_ID;
+ }
+ if (linkedLayer->touchCropId == layer.id) {
+ linkedLayer->touchCropId = UNASSIGNED_LAYER_ID;
+ }
+ }
+ mIdToLayer.erase(it);
+ }
+
+ auto it = mLayers.begin();
+ while (it != mLayers.end()) {
+ RequestedLayerState* layer = it->get();
+ if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
+ ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str());
+ std::iter_swap(it, mLayers.end() - 1);
+ mDestroyedLayers.emplace_back(std::move(mLayers.back()));
+ if (it == mLayers.end() - 1) {
+ it = mLayers.erase(mLayers.end() - 1);
+ } else {
+ mLayers.erase(mLayers.end() - 1);
+ }
+ } else {
+ it++;
+ }
+ }
+}
+
+void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions) {
+ for (const auto& transaction : transactions) {
+ for (const auto& resolvedComposerState : transaction.states) {
+ const auto& clientState = resolvedComposerState.state;
+ uint32_t layerId = LayerHandle::getLayerId(clientState.surface);
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get());
+ continue;
+ }
+
+ RequestedLayerState* layer = getLayerFromId(layerId);
+ if (layer == nullptr) {
+ LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__,
+ clientState.surface.get(), layerId);
+ continue;
+ }
+
+ if (!layer->handleAlive) {
+ LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of "
+ "order LayerLifecycleManager updates",
+ __func__, clientState.surface.get(), layerId);
+ continue;
+ }
+
+ uint32_t oldParentId = layer->parentId;
+ uint32_t oldRelativeParentId = layer->relativeParentId;
+ uint32_t oldTouchCropId = layer->touchCropId;
+ layer->merge(resolvedComposerState);
+
+ if (layer->what & layer_state_t::eBackgroundColorChanged) {
+ if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColorAlpha != 0) {
+ LayerCreationArgs backgroundLayerArgs{nullptr,
+ nullptr,
+ layer->name + "BackgroundColorLayer",
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ {}};
+ std::vector<std::unique_ptr<RequestedLayerState>> newLayers;
+ newLayers.emplace_back(
+ std::make_unique<RequestedLayerState>(backgroundLayerArgs));
+ RequestedLayerState* backgroundLayer = newLayers.back().get();
+ backgroundLayer->handleAlive = false;
+ backgroundLayer->parentId = layer->id;
+ backgroundLayer->z = std::numeric_limits<int32_t>::min();
+ backgroundLayer->color.rgb = layer->color.rgb;
+ backgroundLayer->color.a = layer->bgColorAlpha;
+ backgroundLayer->dataspace = layer->bgColorDataspace;
+
+ layer->bgColorLayerId = backgroundLayer->id;
+ addLayers({std::move(newLayers)});
+ } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID &&
+ layer->bgColorAlpha == 0) {
+ RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
+ bgColorLayer->parentId = UNASSIGNED_LAYER_ID;
+ onHandlesDestroyed({layer->bgColorLayerId});
+ } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) {
+ RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
+ bgColorLayer->color.rgb = layer->color.rgb;
+ bgColorLayer->color.a = layer->bgColorAlpha;
+ bgColorLayer->dataspace = layer->bgColorDataspace;
+ mGlobalChanges |= RequestedLayerState::Changes::Content;
+ }
+ }
+
+ if (oldParentId != layer->parentId) {
+ unlinkLayer(oldParentId, layer->id);
+ layer->parentId = linkLayer(layer->parentId, layer->id);
+ }
+ if (oldRelativeParentId != layer->relativeParentId) {
+ unlinkLayer(oldRelativeParentId, layer->id);
+ layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id);
+ }
+ if (oldTouchCropId != layer->touchCropId) {
+ unlinkLayer(oldTouchCropId, layer->id);
+ layer->touchCropId = linkLayer(layer->touchCropId, layer->id);
+ }
+
+ mGlobalChanges |= layer->changes &
+ (RequestedLayerState::Changes::Hierarchy |
+ RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::Content);
+ }
+ }
+}
+
+void LayerLifecycleManager::commitChanges() {
+ for (auto& layer : mLayers) {
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ for (auto listener : mListeners) {
+ listener->onLayerAdded(*layer);
+ }
+ }
+ layer->what = 0;
+ layer->changes.clear();
+ }
+
+ for (auto& destroyedLayer : mDestroyedLayers) {
+ if (destroyedLayer->changes.test(RequestedLayerState::Changes::Created)) {
+ for (auto listener : mListeners) {
+ listener->onLayerAdded(*destroyedLayer);
+ }
+ }
+
+ for (auto listener : mListeners) {
+ listener->onLayerDestroyed(*destroyedLayer);
+ }
+ }
+ mDestroyedLayers.clear();
+ mGlobalChanges.clear();
+}
+
+void LayerLifecycleManager::addLifecycleListener(std::shared_ptr<ILifecycleListener> listener) {
+ mListeners.emplace_back(std::move(listener));
+}
+
+void LayerLifecycleManager::removeLifecycleListener(std::shared_ptr<ILifecycleListener> listener) {
+ swapErase(mListeners, listener);
+}
+
+const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getLayers() const {
+ return mLayers;
+}
+
+const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getDestroyedLayers()
+ const {
+ return mDestroyedLayers;
+}
+
+const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const {
+ return mGlobalChanges;
+}
+
+RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) {
+ if (id == UNASSIGNED_LAYER_ID) {
+ return nullptr;
+ }
+ auto it = mIdToLayer.find(id);
+ if (it == mIdToLayer.end()) {
+ return nullptr;
+ }
+ return &it->second.owner;
+}
+
+std::vector<uint32_t>* LayerLifecycleManager::getLinkedLayersFromId(uint32_t id) {
+ if (id == UNASSIGNED_LAYER_ID) {
+ return nullptr;
+ }
+ auto it = mIdToLayer.find(id);
+ if (it == mIdToLayer.end()) {
+ return nullptr;
+ }
+ return &it->second.references;
+}
+
+uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ return UNASSIGNED_LAYER_ID;
+ }
+
+ std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+ if (!linkedLayers) {
+ ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId,
+ layerToLink);
+ return UNASSIGNED_LAYER_ID;
+ }
+ linkedLayers->emplace_back(layerToLink);
+ return layerId;
+}
+
+uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) {
+ std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+ if (!linkedLayers) {
+ return UNASSIGNED_LAYER_ID;
+ }
+ swapErase(*linkedLayers, linkedLayer);
+ return UNASSIGNED_LAYER_ID;
+}
+
+std::string LayerLifecycleManager::References::getDebugString() const {
+ std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:";
+ std::for_each(references.begin(), references.end(),
+ [&debugInfo = debugInfo](const uint32_t& reference) mutable {
+ debugInfo += std::to_string(reference) + ",";
+ });
+ return debugInfo;
+}
+
+void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) {
+ auto it = mIdToLayer.find(relativeRootId);
+ if (it == mIdToLayer.end()) {
+ return;
+ }
+ RequestedLayerState& layer = it->second.owner;
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.changes |=
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent;
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
new file mode 100644
index 0000000..63a7afc
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -0,0 +1,101 @@
+/*
+ * 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 "RequestedLayerState.h"
+#include "TransactionState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Owns a collection of RequestedLayerStates and manages their lifecycle
+// and state changes.
+//
+// RequestedLayerStates are tracked and destroyed if they have no parent and
+// no handle left to keep them alive. The handle does not keep a reference to
+// the RequestedLayerState but a layer id associated with the RequestedLayerState.
+// If the handle is destroyed and the RequestedLayerState does not have a parent,
+// the LayerLifecycleManager destroys the RequestedLayerState.
+//
+// Threading: This class is not thread safe, it requires external synchronization.
+//
+// Typical usage: Input states (new layers, transactions, destroyed layer handles)
+// are collected in the background passed into the LayerLifecycleManager to update
+// layer lifecycle and layer state at start of composition.
+class LayerLifecycleManager {
+public:
+ // External state changes should be updated in the following order:
+ void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>);
+ void applyTransactions(const std::vector<TransactionState>&);
+ void onHandlesDestroyed(const std::vector<uint32_t>&);
+
+ // Detaches the layer from its relative parent to prevent a loop in the
+ // layer hierarchy. This overrides the RequestedLayerState and leaves
+ // the system in an invalid state. This is always a client error that
+ // needs to be fixed but overriding the state allows us to fail gracefully.
+ void fixRelativeZLoop(uint32_t relativeRootId);
+
+ // Destroys RequestedLayerStates that are marked to be destroyed. Invokes all
+ // ILifecycleListener callbacks and clears any change flags from previous state
+ // updates. This function should be called outside the hot path since it's not
+ // critical to composition.
+ void commitChanges();
+
+ class ILifecycleListener {
+ public:
+ virtual ~ILifecycleListener() = default;
+ // Called on commitChanges when a layer is added. The callback includes
+ // the layer state the client was created with as well as any state updates
+ // until changes were committed.
+ virtual void onLayerAdded(const RequestedLayerState&) = 0;
+ // Called on commitChanges when a layer has been destroyed. The callback
+ // includes the final state before the layer was destroyed.
+ virtual void onLayerDestroyed(const RequestedLayerState&) = 0;
+ };
+ void addLifecycleListener(std::shared_ptr<ILifecycleListener>);
+ void removeLifecycleListener(std::shared_ptr<ILifecycleListener>);
+ const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const;
+ const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const;
+ const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
+
+private:
+ friend class LayerLifecycleManagerTest;
+ friend class HierarchyBuilderTest;
+ friend class android::SurfaceFlinger;
+
+ RequestedLayerState* getLayerFromId(uint32_t);
+ std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
+ uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
+ uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
+
+ struct References {
+ // Lifetime tied to mLayers
+ RequestedLayerState& owner;
+ std::vector<uint32_t> references;
+ std::string getDebugString() const;
+ };
+ std::unordered_map<uint32_t, References> mIdToLayer;
+ // Listeners are invoked once changes are committed.
+ std::vector<std::shared_ptr<ILifecycleListener>> mListeners;
+
+ // Aggregation of changes since last commit.
+ ftl::Flags<RequestedLayerState::Changes> mGlobalChanges;
+ std::vector<std::unique_ptr<RequestedLayerState>> mLayers;
+ // Layers pending destruction. Layers will be destroyed once changes are committed.
+ std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
new file mode 100644
index 0000000..054382c
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -0,0 +1,370 @@
+/*
+ * 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 "FrontEnd/LayerCreationArgs.h"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "RequestedLayerState"
+
+#include <private/android_filesystem_config.h>
+#include <sys/types.h>
+
+#include "Layer.h"
+#include "LayerHandle.h"
+#include "RequestedLayerState.h"
+
+namespace android::surfaceflinger::frontend {
+using ftl::Flags;
+using namespace ftl::flag_operators;
+
+namespace {
+uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
+ if (!surfaceControl) {
+ return UNASSIGNED_LAYER_ID;
+ }
+
+ return LayerHandle::getLayerId(surfaceControl->getHandle());
+}
+
+std::string layerIdToString(uint32_t layerId) {
+ return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
+}
+
+} // namespace
+
+RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
+ : id(args.sequence),
+ name(args.name),
+ canBeRoot(args.addToRoot),
+ layerCreationFlags(args.flags),
+ textureName(args.textureName),
+ ownerUid(args.ownerUid),
+ ownerPid(args.ownerPid) {
+ layerId = static_cast<int32_t>(args.sequence);
+ changes |= RequestedLayerState::Changes::Created;
+ metadata.merge(args.metadata);
+ changes |= RequestedLayerState::Changes::Metadata;
+ handleAlive = true;
+ parentId = LayerHandle::getLayerId(args.parentHandle.promote());
+ mirrorId = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
+ if (mirrorId != UNASSIGNED_LAYER_ID) {
+ changes |= RequestedLayerState::Changes::Mirror;
+ }
+
+ flags = 0;
+ if (args.flags & ISurfaceComposerClient::eHidden) flags |= layer_state_t::eLayerHidden;
+ if (args.flags & ISurfaceComposerClient::eOpaque) flags |= layer_state_t::eLayerOpaque;
+ if (args.flags & ISurfaceComposerClient::eSecure) flags |= layer_state_t::eLayerSecure;
+ if (args.flags & ISurfaceComposerClient::eSkipScreenshot) {
+ flags |= layer_state_t::eLayerSkipScreenshot;
+ }
+ premultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+ potentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+ protectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
+ if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+ // Set an invalid color so there is no color fill.
+ // (b/259981098) use an explicit flag instead of relying on invalid values.
+ color.r = -1.0_hf;
+ color.g = -1.0_hf;
+ color.b = -1.0_hf;
+ } else {
+ color.rgb = {0.0_hf, 0.0_hf, 0.0_hf};
+ }
+ color.a = 1.0f;
+
+ crop.makeInvalid();
+ z = 0;
+ layerStack = ui::DEFAULT_LAYER_STACK;
+ transformToDisplayInverse = false;
+ dataspace = ui::Dataspace::UNKNOWN;
+ dataspaceRequested = false;
+ hdrMetadata.validTypes = 0;
+ surfaceDamageRegion = Region::INVALID_REGION;
+ cornerRadius = 0.0f;
+ backgroundBlurRadius = 0;
+ api = -1;
+ hasColorTransform = false;
+ bufferTransform = 0;
+ requestedTransform.reset();
+ bufferData = std::make_shared<BufferData>();
+ bufferData->frameNumber = 0;
+ bufferData->acquireFence = sp<Fence>::make(-1);
+ acquireFenceTime = std::make_shared<FenceTime>(bufferData->acquireFence);
+ colorSpaceAgnostic = false;
+ frameRateSelectionPriority = Layer::PRIORITY_UNSET;
+ shadowRadius = 0.f;
+ fixedTransformHint = ui::Transform::ROT_INVALID;
+ destinationFrame.makeInvalid();
+ isTrustedOverlay = false;
+ dropInputMode = gui::DropInputMode::NONE;
+ dimmingEnabled = true;
+ defaultFrameRateCompatibility =
+ static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
+ dataspace = ui::Dataspace::V0_SRGB;
+}
+
+void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
+ bool oldFlags = flags;
+ Rect oldBufferSize = getBufferSize();
+ const layer_state_t& clientState = resolvedComposerState.state;
+
+ uint64_t clientChanges = what | layer_state_t::diff(clientState);
+ layer_state_t::merge(clientState);
+ what = clientChanges;
+
+ if (clientState.what & layer_state_t::eFlagsChanged) {
+ if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
+ changes |= RequestedLayerState::Changes::Visibility;
+ }
+ if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) {
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+ }
+ if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize()) {
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+ if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
+ changes |= RequestedLayerState::Changes::Hierarchy;
+ if (clientChanges & layer_state_t::CONTENT_CHANGES)
+ changes |= RequestedLayerState::Changes::Content;
+ if (clientChanges & layer_state_t::GEOMETRY_CHANGES)
+ changes |= RequestedLayerState::Changes::Geometry;
+
+ if (clientState.what & layer_state_t::eColorTransformChanged) {
+ static const mat4 identityMatrix = mat4();
+ hasColorTransform = colorTransform != identityMatrix;
+ }
+ if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
+ changes |= RequestedLayerState::Changes::Z;
+ }
+ if (clientState.what & layer_state_t::eReparent) {
+ changes |= RequestedLayerState::Changes::Parent;
+ parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
+ parentSurfaceControlForChild = nullptr;
+ // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
+ // but thats the existing logic and until we make this behavior more explicit, we need
+ // to maintain this logic.
+ canBeRoot = false;
+ }
+ if (clientState.what & layer_state_t::eRelativeLayerChanged) {
+ changes |= RequestedLayerState::Changes::RelativeParent;
+ relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl);
+ isRelativeOf = true;
+ relativeLayerSurfaceControl = nullptr;
+ }
+ if ((clientState.what & layer_state_t::eLayerChanged ||
+ (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) &&
+ isRelativeOf) {
+ // clear out relz data
+ relativeParentId = UNASSIGNED_LAYER_ID;
+ isRelativeOf = false;
+ changes |= RequestedLayerState::Changes::RelativeParent;
+ }
+ if (clientState.what & layer_state_t::eReparent && parentId == relativeParentId) {
+ // provide a hint that we are are now a direct child and not a relative child.
+ changes |= RequestedLayerState::Changes::RelativeParent;
+ }
+ if (clientState.what & layer_state_t::eInputInfoChanged) {
+ wp<IBinder>& touchableRegionCropHandle =
+ windowInfoHandle->editInfo()->touchableRegionCropHandle;
+ touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote());
+ changes |= RequestedLayerState::Changes::Input;
+ touchableRegionCropHandle.clear();
+ }
+ if (clientState.what & layer_state_t::eStretchChanged) {
+ stretchEffect.sanitize();
+ }
+
+ if (clientState.what & layer_state_t::eHasListenerCallbacksChanged) {
+ // TODO(b/238781169) handle callbacks
+ }
+
+ if (clientState.what & layer_state_t::eBufferChanged) {
+ externalTexture = resolvedComposerState.externalTexture;
+ }
+
+ if (clientState.what & layer_state_t::ePositionChanged) {
+ requestedTransform.set(x, y);
+ }
+
+ if (clientState.what & layer_state_t::eMatrixChanged) {
+ requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+ }
+}
+
+ui::Transform RequestedLayerState::getTransform() const {
+ if ((flags & layer_state_t::eIgnoreDestinationFrame) || destinationFrame.isEmpty()) {
+ // If destination frame is not set, use the requested transform set via
+ // Transaction::setPosition and Transaction::setMatrix.
+ return requestedTransform;
+ }
+
+ Rect destRect = destinationFrame;
+ int32_t destW = destRect.width();
+ int32_t destH = destRect.height();
+ if (destRect.left < 0) {
+ destRect.left = 0;
+ destRect.right = destW;
+ }
+ if (destRect.top < 0) {
+ destRect.top = 0;
+ destRect.bottom = destH;
+ }
+
+ if (!externalTexture) {
+ ui::Transform transform;
+ transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top));
+ return transform;
+ }
+
+ uint32_t bufferWidth = externalTexture->getWidth();
+ uint32_t bufferHeight = externalTexture->getHeight();
+ // Undo any transformations on the buffer.
+ if (bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ // TODO(b/238781169) remove dep
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (transformToDisplayInverse) {
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ float sx = static_cast<float>(destW) / static_cast<float>(bufferWidth);
+ float sy = static_cast<float>(destH) / static_cast<float>(bufferHeight);
+ ui::Transform transform;
+ transform.set(sx, 0, 0, sy);
+ transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top));
+ return transform;
+}
+
+std::string RequestedLayerState::getDebugString() const {
+ return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) +
+ ",relativeParent=" + layerIdToString(relativeParentId) +
+ ",isRelativeOf=" + std::to_string(isRelativeOf) +
+ ",mirrorId=" + layerIdToString(mirrorId) +
+ ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
+}
+
+std::string RequestedLayerState::getDebugStringShort() const {
+ return "[" + std::to_string(id) + "]" + name;
+}
+
+bool RequestedLayerState::canBeDestroyed() const {
+ return !handleAlive && parentId == UNASSIGNED_LAYER_ID;
+}
+bool RequestedLayerState::isRoot() const {
+ return canBeRoot && parentId == UNASSIGNED_LAYER_ID;
+}
+bool RequestedLayerState::isHiddenByPolicy() const {
+ return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden;
+};
+half4 RequestedLayerState::getColor() const {
+ if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ return {0._hf, 0._hf, 0._hf, color.a};
+ }
+ return color;
+}
+Rect RequestedLayerState::getBufferSize() const {
+ // for buffer state layers we use the display frame size as the buffer size.
+ if (!externalTexture) {
+ return Rect::INVALID_RECT;
+ }
+
+ uint32_t bufWidth = externalTexture->getWidth();
+ uint32_t bufHeight = externalTexture->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (transformToDisplayInverse) {
+ // TODO(b/238781169) pass in display metrics (would be useful for input info as well
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
+}
+
+Rect RequestedLayerState::getCroppedBufferSize() const {
+ Rect size = getBufferSize();
+ if (!crop.isEmpty() && size.isValid()) {
+ size.intersect(crop, &size);
+ } else if (!crop.isEmpty()) {
+ size = crop;
+ }
+ return size;
+}
+
+Rect RequestedLayerState::getBufferCrop() const {
+ // this is the crop rectangle that applies to the buffer
+ // itself (as opposed to the window)
+ if (!bufferCrop.isEmpty()) {
+ // if the buffer crop is defined, we use that
+ return bufferCrop;
+ } else if (externalTexture != nullptr) {
+ // otherwise we use the whole buffer
+ return externalTexture->getBounds();
+ } else {
+ // if we don't have a buffer yet, we use an empty/invalid crop
+ return Rect();
+ }
+}
+
+aidl::android::hardware::graphics::composer3::Composition RequestedLayerState::getCompositionType()
+ const {
+ using aidl::android::hardware::graphics::composer3::Composition;
+ // TODO(b/238781169) check about sidestream ready flag
+ if (sidebandStream.get()) {
+ return Composition::SIDEBAND;
+ }
+ if (!externalTexture) {
+ return Composition::SOLID_COLOR;
+ }
+ if (flags & layer_state_t::eLayerIsDisplayDecoration) {
+ return Composition::DISPLAY_DECORATION;
+ }
+ if (potentialCursor) {
+ return Composition::CURSOR;
+ }
+ return Composition::DEVICE;
+}
+
+Rect RequestedLayerState::reduce(const Rect& win, const Region& exclude) {
+ if (CC_LIKELY(exclude.isEmpty())) {
+ return win;
+ }
+ if (exclude.isRect()) {
+ return win.reduce(exclude.getBounds());
+ }
+ return Region(win).subtract(exclude).getBounds();
+}
+
+// Returns true if the layer has a relative parent that is not its own parent. This is an input
+// error from the client, and this check allows us to handle it gracefully. If both parentId and
+// relativeParentId is unassigned then the layer does not have a valid relative parent.
+// If the relative parentid is unassigned, the layer will be considered relative but won't be
+// reachable.
+bool RequestedLayerState::hasValidRelativeParent() const {
+ return isRelativeOf && parentId != relativeParentId;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
new file mode 100644
index 0000000..7849165
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -0,0 +1,102 @@
+/*
+ * 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 <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <ftl/flags.h>
+#include <gui/LayerState.h>
+#include <renderengine/ExternalTexture.h>
+
+#include "LayerCreationArgs.h"
+#include "TransactionState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Stores client requested states for a layer.
+// This struct does not store any other states or states pertaining to
+// other layers. Links to other layers that are part of the client
+// requested state such as parent are translated to layer id so
+// we can avoid extending the lifetime of layer handles.
+struct RequestedLayerState : layer_state_t {
+ // Changes in state after merging with new state. This includes additional state
+ // changes found in layer_state_t::what.
+ enum class Changes : uint32_t {
+ Created = 1u << 0,
+ Destroyed = 1u << 1,
+ Hierarchy = 1u << 2,
+ Geometry = 1u << 3,
+ Content = 1u << 4,
+ Input = 1u << 5,
+ Z = 1u << 6,
+ Mirror = 1u << 7,
+ Parent = 1u << 8,
+ RelativeParent = 1u << 9,
+ Metadata = 1u << 10,
+ Visibility = 1u << 11,
+ };
+ static Rect reduce(const Rect& win, const Region& exclude);
+ RequestedLayerState(const LayerCreationArgs&);
+ void merge(const ResolvedComposerState&);
+ ui::Transform getTransform() const;
+ bool canBeDestroyed() const;
+ bool isRoot() const;
+ bool isHiddenByPolicy() const;
+ half4 getColor() const;
+ Rect getBufferSize() const;
+ Rect getCroppedBufferSize() const;
+ Rect getBufferCrop() const;
+ std::string getDebugString() const;
+ std::string getDebugStringShort() const;
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
+ bool hasValidRelativeParent() const;
+
+ // Layer serial number. This gives layers an explicit ordering, so we
+ // have a stable sort order when their layer stack and Z-order are
+ // the same.
+ const uint32_t id;
+ const std::string name;
+ bool canBeRoot = false;
+ const uint32_t layerCreationFlags;
+ const uint32_t textureName;
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ const uid_t ownerUid;
+ // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+ // If created from a system process, the value can be passed in.
+ const pid_t ownerPid;
+ bool dataspaceRequested;
+ bool hasColorTransform;
+ bool premultipliedAlpha{true};
+ // This layer can be a cursor on some displays.
+ bool potentialCursor{false};
+ bool protectedByApp{false}; // application requires protected path to external sink
+ ui::Transform requestedTransform;
+ std::shared_ptr<FenceTime> acquireFenceTime;
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+
+ // book keeping states
+ bool handleAlive = true;
+ bool isRelativeOf = false;
+ uint32_t parentId = UNASSIGNED_LAYER_ID;
+ uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
+ uint32_t mirrorId = UNASSIGNED_LAYER_ID;
+ uint32_t touchCropId = UNASSIGNED_LAYER_ID;
+ uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
+ ftl::Flags<RequestedLayerState::Changes> changes;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h
new file mode 100644
index 0000000..f672f99
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/SwapErase.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <vector>
+
+namespace android::surfaceflinger::frontend {
+// Erases the first element in vec that matches value. This is a more optimal way to
+// remove an element from a vector that avoids relocating all the elements after the one
+// that is erased.
+template <typename T>
+void swapErase(std::vector<T>& vec, const T& value) {
+ auto it = std::find(vec.begin(), vec.end(), value);
+ if (it != vec.end()) {
+ std::iter_swap(it, vec.end() - 1);
+ vec.erase(vec.end() - 1);
+ }
+}
+
+// Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element
+// that returns true for predicate.
+template <typename T, class P>
+void swapErase(std::vector<T>& vec, P predicate) {
+ auto it = std::find_if(vec.begin(), vec.end(), predicate);
+ if (it != vec.end()) {
+ std::iter_swap(it, vec.end() - 1);
+ vec.erase(vec.end() - 1);
+ }
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
new file mode 100644
index 0000000..c2109b3
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionHandler"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cutils/trace.h>
+#include <utils/Log.h>
+
+#include "TransactionHandler.h"
+
+namespace android::surfaceflinger::frontend {
+
+void TransactionHandler::queueTransaction(TransactionState&& state) {
+ mLocklessTransactionQueue.push(std::move(state));
+ mPendingTransactionCount.fetch_add(1);
+ ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+}
+
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
+ while (!mLocklessTransactionQueue.isEmpty()) {
+ auto maybeTransaction = mLocklessTransactionQueue.pop();
+ if (!maybeTransaction.has_value()) {
+ break;
+ }
+ auto transaction = maybeTransaction.value();
+ mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
+ }
+
+ // Collect transaction that are ready to be applied.
+ std::vector<TransactionState> transactions;
+ TransactionFlushState flushState;
+ flushState.queueProcessTime = systemTime();
+ // Transactions with a buffer pending on a barrier may be on a different applyToken
+ // than the transaction which satisfies our barrier. In fact this is the exact use case
+ // that the primitive is designed for. This means we may first process
+ // the barrier dependent transaction, determine it ineligible to complete
+ // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+ // The barrier dependent transaction was eligible to be presented in this frame
+ // but we would have prevented it without case. To fix this we continually
+ // loop through flushPendingTransactionQueues until we perform an iteration
+ // where the number of transactionsPendingBarrier doesn't change. This way
+ // we can continue to resolve dependency chains of barriers as far as possible.
+ int lastTransactionsPendingBarrier = 0;
+ int transactionsPendingBarrier = 0;
+ do {
+ lastTransactionsPendingBarrier = transactionsPendingBarrier;
+ // Collect transactions that are ready to be applied.
+ transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState);
+ } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
+
+ mPendingTransactionCount.fetch_sub(transactions.size());
+ ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+ return transactions;
+}
+
+TransactionHandler::TransactionReadiness TransactionHandler::applyFilters(
+ TransactionFlushState& flushState) {
+ auto ready = TransactionReadiness::Ready;
+ for (auto& filter : mTransactionReadyFilters) {
+ auto perFilterReady = filter(flushState);
+ switch (perFilterReady) {
+ case TransactionReadiness::NotReady:
+ case TransactionReadiness::NotReadyBarrier:
+ return perFilterReady;
+
+ case TransactionReadiness::ReadyUnsignaled:
+ case TransactionReadiness::ReadyUnsignaledSingle:
+ // If one of the filters allows latching an unsignaled buffer, latch this ready
+ // state.
+ ready = perFilterReady;
+ break;
+ case TransactionReadiness::Ready:
+ continue;
+ }
+ }
+ return ready;
+}
+
+int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
+ TransactionFlushState& flushState) {
+ int transactionsPendingBarrier = 0;
+ auto it = mPendingTransactionQueues.begin();
+ while (it != mPendingTransactionQueues.end()) {
+ auto& queue = it->second;
+ IBinder* queueToken = it->first.get();
+
+ // if we have already flushed a transaction with an unsignaled buffer then stop queue
+ // processing
+ if (std::find(flushState.queuesWithUnsignaledBuffers.begin(),
+ flushState.queuesWithUnsignaledBuffers.end(),
+ queueToken) != flushState.queuesWithUnsignaledBuffers.end()) {
+ continue;
+ }
+
+ while (!queue.empty()) {
+ auto& transaction = queue.front();
+ flushState.transaction = &transaction;
+ auto ready = applyFilters(flushState);
+ if (ready == TransactionReadiness::NotReadyBarrier) {
+ transactionsPendingBarrier++;
+ break;
+ } else if (ready == TransactionReadiness::NotReady) {
+ break;
+ }
+
+ // Transaction is ready move it from the pending queue.
+ flushState.firstTransaction = false;
+ removeFromStalledTransactions(transaction.id);
+ transactions.emplace_back(std::move(transaction));
+ queue.pop();
+
+ // If the buffer is unsignaled, then we don't want to signal other transactions using
+ // the buffer as a barrier.
+ auto& readyToApplyTransaction = transactions.back();
+ if (ready == TransactionReadiness::Ready) {
+ readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+ const bool frameNumberChanged = state.bufferData->flags.test(
+ BufferData::BufferDataChange::frameNumberChanged);
+ if (frameNumberChanged) {
+ flushState.bufferLayersReadyToPresent
+ .emplace_or_replace(state.surface.get(),
+ state.bufferData->frameNumber);
+ } else {
+ // Barrier function only used for BBQ which always includes a frame number.
+ // This value only used for barrier logic.
+ flushState.bufferLayersReadyToPresent
+ .emplace_or_replace(state.surface.get(),
+ std::numeric_limits<uint64_t>::max());
+ }
+ });
+ } else if (ready == TransactionReadiness::ReadyUnsignaledSingle) {
+ // Track queues with a flushed unsingaled buffer.
+ flushState.queuesWithUnsignaledBuffers.emplace_back(queueToken);
+ break;
+ }
+ }
+
+ if (queue.empty()) {
+ it = mPendingTransactionQueues.erase(it);
+ } else {
+ it = std::next(it, 1);
+ }
+ }
+ return transactionsPendingBarrier;
+}
+
+void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) {
+ mTransactionReadyFilters.emplace_back(std::move(filter));
+}
+
+bool TransactionHandler::hasPendingTransactions() {
+ return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+}
+
+void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId,
+ sp<ITransactionCompletedListener>& listener,
+ const std::string& reason) {
+ if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) !=
+ mStalledTransactions.end()) {
+ return;
+ }
+
+ mStalledTransactions.push_back(transactionId);
+ listener->onTransactionQueueStalled(reason);
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
+ auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
+ if (it != mStalledTransactions.end()) {
+ mStalledTransactions.erase(it);
+ }
+}
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
new file mode 100644
index 0000000..475ff1b
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -0,0 +1,77 @@
+/*
+ * 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 <semaphore.h>
+#include <cstdint>
+#include <vector>
+
+#include <LocklessQueue.h>
+#include <TransactionState.h>
+#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
+
+namespace android {
+
+class TestableSurfaceFlinger;
+using gui::IListenerHash;
+namespace surfaceflinger::frontend {
+
+class TransactionHandler {
+public:
+ struct TransactionFlushState {
+ const TransactionState* transaction;
+ bool firstTransaction = true;
+ nsecs_t queueProcessTime = 0;
+ // Layer handles that have transactions with buffers that are ready to be applied.
+ ftl::SmallMap<IBinder* /* binder address */, uint64_t /* framenumber */, 15>
+ bufferLayersReadyToPresent = {};
+ ftl::SmallVector<IBinder* /* queueToken */, 15> queuesWithUnsignaledBuffers;
+ };
+ enum class TransactionReadiness {
+ NotReady,
+ NotReadyBarrier,
+ Ready,
+ ReadyUnsignaled,
+ ReadyUnsignaledSingle,
+ };
+ using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
+
+ bool hasPendingTransactions();
+ std::vector<TransactionState> flushTransactions();
+ void addTransactionReadyFilter(TransactionFilter&&);
+ void queueTransaction(TransactionState&&);
+ void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&,
+ const std::string& reason);
+ void removeFromStalledTransactions(uint64_t transactionId);
+
+private:
+ // For unit tests
+ friend class ::android::TestableSurfaceFlinger;
+
+ int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
+ TransactionReadiness applyFilters(TransactionFlushState&);
+ std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+ mPendingTransactionQueues;
+ LocklessQueue<TransactionState> mLocklessTransactionQueue;
+ std::atomic<size_t> mPendingTransactionCount = 0;
+ ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
+ std::vector<uint64_t> mStalledTransactions;
+};
+} // namespace surfaceflinger::frontend
+} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.cpp b/services/surfaceflinger/HwcSlotGenerator.cpp
deleted file mode 100644
index 939c35b..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "HwcSlotGenerator"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-HwcSlotGenerator::HwcSlotGenerator() {
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mFreeHwcCacheSlots.push(i);
- }
-}
-
-void HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
- std::lock_guard lock(mMutex);
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, failed to erase buffer");
- return;
- }
- eraseBufferLocked(clientCacheId);
-}
-
-int HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- std::lock_guard<std::mutex> lock(mMutex);
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return addCachedBuffer(clientCacheId);
- }
- auto& [hwcCacheSlot, counter] = itr->second;
- counter = mCounter++;
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, returning invalid slot");
- return BufferQueue::INVALID_BUFFER_SLOT;
- }
-
- ClientCache::getInstance().registerErasedRecipient(clientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-
- int hwcCacheSlot = getFreeHwcCacheSlot();
- mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
- if (mFreeHwcCacheSlots.empty()) {
- evictLeastRecentlyUsed();
- }
-
- int hwcCacheSlot = mFreeHwcCacheSlots.top();
- mFreeHwcCacheSlots.pop();
- return hwcCacheSlot;
-}
-
-void HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
- uint64_t minCounter = UINT_MAX;
- client_cache_t minClientCacheId = {};
- for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
- const auto& [hwcCacheSlot, counter] = slotCounter;
- if (counter < minCounter) {
- minCounter = counter;
- minClientCacheId = clientCacheId;
- }
- }
- eraseBufferLocked(minClientCacheId);
-
- ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-}
-
-void HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return;
- }
- auto& [hwcCacheSlot, counter] = itr->second;
-
- // TODO send to hwc cache and resources
-
- mFreeHwcCacheSlots.push(hwcCacheSlot);
- mCachedBuffers.erase(clientCacheId);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.h b/services/surfaceflinger/HwcSlotGenerator.h
deleted file mode 100644
index 5a1b6d7..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 <functional>
-#include <mutex>
-#include <stack>
-#include <unordered_map>
-
-#include "ClientCache.h"
-
-namespace android {
-
-class HwcSlotGenerator : public ClientCache::ErasedRecipient {
-public:
- HwcSlotGenerator();
- void bufferErased(const client_cache_t& clientCacheId);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
-
-private:
- friend class SlotGenerationTest;
- int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
- int getFreeHwcCacheSlot() REQUIRES(mMutex);
- void evictLeastRecentlyUsed() REQUIRES(mMutex);
- void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-
- struct CachedBufferHash {
- std::size_t operator()(const client_cache_t& clientCacheId) const {
- return std::hash<uint64_t>{}(clientCacheId.id);
- }
- };
-
- std::mutex mMutex;
-
- std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
- CachedBufferHash>
- mCachedBuffers GUARDED_BY(mMutex);
- std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
-
- // The cache increments this counter value when a slot is updated or used.
- // Used to track the least recently-used buffer
- uint64_t mCounter = 0;
-};
-} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 410e438..b7abd95 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -40,7 +40,6 @@
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <gui/BufferItem.h>
-#include <gui/GLConsumer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <gui/TraceUtils.h>
@@ -63,12 +62,15 @@
#include <algorithm>
#include <mutex>
+#include <optional>
#include <sstream>
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "LayerProtoHelper.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
@@ -81,28 +83,8 @@
namespace {
constexpr int kDumpTableRowLength = 159;
-static constexpr float defaultMaxLuminance = 1000.0;
-
const ui::Transform kIdentityTransform;
-constexpr mat4 inverseOrientation(uint32_t transform) {
- const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
- const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
- mat4 tr;
-
- if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- tr = tr * rot90;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
- tr = tr * flipH;
- }
- if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
- tr = tr * flipV;
- }
- return inverse(tr);
-}
-
bool assignTransform(ui::Transform* dst, ui::Transform& from) {
if (*dst == from) {
return false;
@@ -152,10 +134,8 @@
using PresentState = frametimeline::SurfaceFrame::PresentState;
-std::atomic<int32_t> Layer::sSequence{1};
-
Layer::Layer(const LayerCreationArgs& args)
- : sequence(args.sequence.value_or(sSequence++)),
+ : sequence(args.sequence),
mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)),
mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
mClientRef(args.client),
@@ -164,7 +144,7 @@
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
mTextureName(args.textureName),
- mHwcSlotGenerator(sp<HwcSlotGenerator>::make()) {
+ mLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
uint32_t layerFlags = 0;
@@ -173,9 +153,6 @@
if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
layerFlags |= layer_state_t::eLayerSkipScreenshot;
- if (args.sequence) {
- sSequence = *args.sequence + 1;
- }
mDrawingState.flags = layerFlags;
mDrawingState.crop.makeInvalid();
mDrawingState.z = 0;
@@ -217,21 +194,10 @@
mDrawingState.color.b = -1.0_hf;
}
- mFrameTracker.setDisplayRefreshPeriod(
- args.flinger->mScheduler->getVsyncPeriodFromRefreshRateConfigs());
+ mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod());
- mCallingPid = args.callingPid;
- mCallingUid = args.callingUid;
-
- if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
- // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
- mOwnerUid = args.metadata.getInt32(gui::METADATA_OWNER_UID, mCallingUid);
- mOwnerPid = args.metadata.getInt32(gui::METADATA_OWNER_PID, mCallingPid);
- } else {
- // A create layer request from a non system request cannot specify the owner uid
- mOwnerUid = mCallingUid;
- mOwnerPid = mCallingPid;
- }
+ mOwnerUid = args.ownerUid;
+ mOwnerPid = args.ownerPid;
mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
@@ -272,11 +238,6 @@
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
- sp<Client> c(mClientRef.promote());
- if (c != 0) {
- c->detachLayer(this);
- }
-
mFrameTracker.logAndResetStats(mName);
mFlinger->onLayerDestroyed(this);
@@ -288,18 +249,6 @@
}
}
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
- uint32_t flags, LayerMetadata metadata)
- : flinger(flinger),
- client(std::move(client)),
- name(std::move(name)),
- flags(flags),
- metadata(std::move(metadata)) {
- IPCThreadState* ipc = IPCThreadState::self();
- callingPid = ipc->getCallingPid();
- callingUid = ipc->getCallingUid();
-}
-
// ---------------------------------------------------------------------------
// callbacks
// ---------------------------------------------------------------------------
@@ -377,7 +326,7 @@
return nullptr;
}
mGetHandleCalled = true;
- return sp<Handle>::make(mFlinger, sp<Layer>::fromExisting(this));
+ return sp<LayerHandle>::make(mFlinger, sp<Layer>::fromExisting(this));
}
// ---------------------------------------------------------------------------
@@ -521,7 +470,7 @@
snapshot->geomLayerTransform = getTransform();
snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
-
+ snapshot->blurRegionTransform = getActiveTransform(drawingState).inverse();
snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
snapshot->alpha = alpha;
snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
@@ -623,9 +572,6 @@
}
snapshot->buffer = getBuffer();
- snapshot->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
- ? 0
- : mBufferInfo.mBufferSlot;
snapshot->acquireFence = mBufferInfo.mFence;
snapshot->frameNumber = mBufferInfo.mFrameNumber;
snapshot->sidebandStreamHasFrame = false;
@@ -652,12 +598,6 @@
snapshot->cursorFrame = frame;
}
-sp<compositionengine::LayerFE> Layer::asLayerFE() const {
- compositionengine::LayerFE* layerFE = const_cast<compositionengine::LayerFE*>(
- static_cast<const compositionengine::LayerFE*>(this));
- return sp<compositionengine::LayerFE>::fromExisting(layerFE);
-}
-
const char* Layer::getDebugName() const {
return mName.c_str();
}
@@ -666,237 +606,6 @@
// drawing...
// ---------------------------------------------------------------------------
-std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
- prepareClientCompositionInternal(targetSettings);
- // Nothing to render.
- if (!layerSettings) {
- return {};
- }
-
- // HWC requests to clear this layer.
- if (targetSettings.clearContent) {
- prepareClearClientComposition(*layerSettings, false /* blackout */);
- return layerSettings;
- }
-
- // set the shadow for the layer if needed
- prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
-
- return layerSettings;
-}
-
-std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionInternal(
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- ATRACE_CALL();
-
- const auto* snapshot = getLayerSnapshot();
- if (!snapshot) {
- return {};
- }
-
- compositionengine::LayerFE::LayerSettings layerSettings;
- layerSettings.geometry.boundaries =
- reduce(snapshot->geomLayerBounds, snapshot->transparentRegionHint);
- layerSettings.geometry.positionTransform = snapshot->geomLayerTransform.asMatrix4();
-
- // skip drawing content if the targetSettings indicate the content will be occluded
- const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
- layerSettings.skipContentDraw = !drawContent;
-
- if (hasColorTransform()) {
- layerSettings.colorTransform = snapshot->colorTransform;
- }
-
- const auto& roundedCornerState = snapshot->roundedCorner;
- layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
- layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
-
- layerSettings.alpha = snapshot->alpha;
- layerSettings.sourceDataspace = snapshot->dataspace;
-
- // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
- // We do this here instead of in buffer info so that dumpsys can still report layers that are
- // using the 170M transfer.
- if (mFlinger->mTreat170mAsSrgb &&
- (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
- HAL_DATASPACE_TRANSFER_SMPTE_170M) {
- layerSettings.sourceDataspace = static_cast<ui::Dataspace>(
- (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) |
- (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) |
- HAL_DATASPACE_TRANSFER_SRGB);
- }
-
- layerSettings.whitePointNits = targetSettings.whitePointNits;
- switch (targetSettings.blurSetting) {
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
- layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
- layerSettings.blurRegions = snapshot->blurRegions;
- layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
- break;
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
- layerSettings.backgroundBlurRadius = snapshot->backgroundBlurRadius;
- break;
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
- layerSettings.blurRegions = snapshot->blurRegions;
- layerSettings.blurRegionTransform = snapshot->geomInverseLayerTransform.asMatrix4();
- break;
- case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
- default:
- break;
- }
- layerSettings.stretchEffect = snapshot->stretchEffect;
- // Record the name of the layer for debugging further down the stack.
- layerSettings.name = snapshot->name;
-
- if (hasEffect() && !hasBufferOrSidebandStream()) {
- prepareEffectsClientComposition(layerSettings, targetSettings);
- return layerSettings;
- }
-
- prepareBufferStateClientComposition(layerSettings, targetSettings);
- return layerSettings;
-}
-
-void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
- bool blackout) const {
- layerSettings.source.buffer.buffer = nullptr;
- layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
- layerSettings.disableBlending = true;
- layerSettings.bufferId = 0;
- layerSettings.frameNumber = 0;
-
- // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
- layerSettings.alpha = blackout ? 1.0f : 0.0f;
- layerSettings.name = getLayerSnapshot()->name;
-}
-
-void Layer::prepareEffectsClientComposition(
- compositionengine::LayerFE::LayerSettings& layerSettings,
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- // If fill bounds are occluded or the fill color is invalid skip the fill settings.
- if (targetSettings.realContentIsVisible && fillsColor()) {
- // Set color for color fill settings.
- layerSettings.source.solidColor = getColor().rgb;
- } else if (hasBlur() || drawShadows()) {
- layerSettings.skipContentDraw = true;
- }
-}
-
-void Layer::prepareBufferStateClientComposition(
- compositionengine::LayerFE::LayerSettings& layerSettings,
- compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
- ATRACE_CALL();
- const auto* snapshot = getLayerSnapshot();
- if (CC_UNLIKELY(!snapshot->externalTexture)) {
- // If there is no buffer for the layer or we have sidebandstream where there is no
- // activeBuffer, then we need to return LayerSettings.
- return;
- }
- const bool blackOutLayer =
- (snapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
- ((snapshot->isSecure || snapshot->hasProtectedContent) && !targetSettings.isSecure);
- const bool bufferCanBeUsedAsHwTexture =
- snapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
- if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
- ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
- snapshot->name.c_str());
- prepareClearClientComposition(layerSettings, true /* blackout */);
- return;
- }
-
- layerSettings.source.buffer.buffer = snapshot->externalTexture;
- layerSettings.source.buffer.isOpaque = snapshot->contentOpaque;
- layerSettings.source.buffer.fence = snapshot->acquireFence;
- layerSettings.source.buffer.textureName = snapshot->textureName;
- layerSettings.source.buffer.usePremultipliedAlpha = snapshot->premultipliedAlpha;
- layerSettings.source.buffer.isY410BT2020 = snapshot->isHdrY410;
- bool hasSmpte2086 = snapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
- bool hasCta861_3 = snapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
- float maxLuminance = 0.f;
- if (hasSmpte2086 && hasCta861_3) {
- maxLuminance = std::min(snapshot->hdrMetadata.smpte2086.maxLuminance,
- snapshot->hdrMetadata.cta8613.maxContentLightLevel);
- } else if (hasSmpte2086) {
- maxLuminance = snapshot->hdrMetadata.smpte2086.maxLuminance;
- } else if (hasCta861_3) {
- maxLuminance = snapshot->hdrMetadata.cta8613.maxContentLightLevel;
- } else {
- switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // Behavior-match previous releases for HDR content
- maxLuminance = defaultMaxLuminance;
- break;
- }
- }
- layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
- layerSettings.frameNumber = snapshot->frameNumber;
- layerSettings.bufferId = snapshot->externalTexture->getId();
-
- const bool useFiltering = targetSettings.needsFiltering ||
- snapshot->geomLayerTransform.needsBilinearFiltering() || snapshot->bufferNeedsFiltering;
-
- // Query the texture matrix given our current filtering mode.
- float textureMatrix[16];
- getDrawingTransformMatrix(useFiltering, textureMatrix);
-
- if (snapshot->geomBufferUsesDisplayInverseTransform) {
- /*
- * the code below applies the primary display's inverse transform to
- * the texture transform
- */
- uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
- mat4 tr = inverseOrientation(transform);
-
- /**
- * TODO(b/36727915): This is basically a hack.
- *
- * Ensure that regardless of the parent transformation,
- * this buffer is always transformed from native display
- * orientation to display orientation. For example, in the case
- * of a camera where the buffer remains in native orientation,
- * we want the pixels to always be upright.
- */
- const auto parentTransform = snapshot->transform;
- tr = tr * inverseOrientation(parentTransform.getOrientation());
-
- // and finally apply it to the original texture matrix
- const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
- memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
- }
-
- const Rect win{layerSettings.geometry.boundaries};
- float bufferWidth = snapshot->bufferSize.getWidth();
- float bufferHeight = snapshot->bufferSize.getHeight();
-
- // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
- // been set and there is no parent layer bounds. In that case, the scale is meaningless so
- // ignore them.
- if (!snapshot->bufferSize.isValid()) {
- bufferWidth = float(win.right) - float(win.left);
- bufferHeight = float(win.bottom) - float(win.top);
- }
-
- const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
- const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
- const float translateY = float(win.top) / bufferHeight;
- const float translateX = float(win.left) / bufferWidth;
-
- // Flip y-coordinates because GLConsumer expects OpenGL convention.
- mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
- mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
- mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
- mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
-
- layerSettings.source.buffer.useTextureFiltering = useFiltering;
- layerSettings.source.buffer.textureTransform =
- mat4(static_cast<const float*>(textureMatrix)) * tr;
-
- return;
-}
-
aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
@@ -1056,7 +765,7 @@
}
bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
- sp<Layer> relative = fromHandle(relativeToHandle).promote();
+ sp<Layer> relative = LayerHandle::getLayer(relativeToHandle);
if (relative == nullptr) {
return false;
}
@@ -1296,8 +1005,10 @@
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
-ui::LayerStack Layer::getLayerStack() const {
- if (const auto parent = mDrawingParent.promote()) {
+ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const {
+ bool useDrawing = state == LayerVector::StateSet::Drawing;
+ const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+ if (parent) {
return parent->getLayerStack();
}
return getDrawingState().layerStack;
@@ -1408,7 +1119,7 @@
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
// the same reason we are allowing touch boost for those layers. See
- // RefreshRateConfigs::getBestRefreshRate for more details.
+ // RefreshRateSelector::rankFrameRates for details.
const auto layerVotedWithDefaultCompatibility =
frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
@@ -1551,7 +1262,6 @@
if (fps) {
surfaceFrame->setRenderRate(*fps);
}
- // TODO(b/178542907): Implement onSurfaceFrameCreated for BQLayer as well.
onSurfaceFrameCreated(surfaceFrame);
return surfaceFrame;
}
@@ -1613,6 +1323,10 @@
return usage;
}
+void Layer::skipReportingTransformHint() {
+ mSkipReportingTransformHint = true;
+}
+
void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
transformHint = ui::Transform::ROT_0;
@@ -1754,9 +1468,10 @@
mFrameTracker.getStats(outStats);
}
-void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
- getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
+void Layer::dumpOffscreenDebugInfo(std::string& result) const {
+ std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
+ StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(),
+ mOwnerPid, mOwnerUid);
}
void Layer::onDisconnect() {
@@ -1822,7 +1537,7 @@
bool Layer::reparent(const sp<IBinder>& newParentHandle) {
sp<Layer> newParent;
if (newParentHandle != nullptr) {
- newParent = fromHandle(newParentHandle).promote();
+ newParent = LayerHandle::getLayer(newParentHandle);
if (newParent == nullptr) {
ALOGE("Unable to promote Layer handle");
return false;
@@ -2139,7 +1854,7 @@
return regionsCopy;
}
-Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+RoundedCornerState Layer::getRoundedCornerState() const {
// Get parent settings
RoundedCornerState parentSettings;
const auto& parent = mDrawingParent.promote();
@@ -2180,21 +1895,6 @@
return {};
}
-void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
- const Rect& layerStackRect) const {
- const auto* snapshot = getLayerSnapshot();
- renderengine::ShadowSettings state = snapshot->shadowSettings;
- if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
- return;
- }
-
- // Shift the spot light x-position to the middle of the display and then
- // offset it by casting layer's screen pos.
- state.lightPos.x = (layerStackRect.width() / 2.f) - snapshot->transformedBounds.left;
- state.lightPos.y -= snapshot->transformedBounds.top;
- caster.shadow = state;
-}
-
bool Layer::findInHierarchy(const sp<Layer>& l) {
if (l == this) {
return true;
@@ -2230,7 +1930,8 @@
void Layer::setInputInfo(const WindowInfo& info) {
mDrawingState.inputInfo = info;
- mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
+ mDrawingState.touchableRegionCrop =
+ LayerHandle::getLayer(info.touchableRegionCropHandle.promote());
mDrawingState.modified = true;
mFlinger->mUpdateInputInfo = true;
setTransactionFlags(eTransactionNeeded);
@@ -2877,23 +2578,6 @@
mFlinger->mNumClones++;
}
-const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle");
-
-wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) {
- if (handleBinder == nullptr) {
- return nullptr;
- }
-
- BBinder* b = handleBinder->localBinder();
- if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) {
- return nullptr;
- }
-
- // We can safely cast this binder since its local and we verified its interface descriptor.
- sp<Handle> handle = sp<Handle>::cast(handleBinder);
- return handle->owner;
-}
-
bool Layer::setDropInputMode(gui::DropInputMode mode) {
if (mDrawingState.dropInputMode == mode) {
return false;
@@ -2917,9 +2601,12 @@
return;
}
ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
- listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd,
+ static_cast<int32_t>(currentMaxAcquiredBufferCount));
}
void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
@@ -2973,6 +2660,10 @@
void Layer::onSurfaceFrameCreated(
const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+ if (!hasBufferOrSidebandStreamInDrawing()) {
+ return;
+ }
+
while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
// Too many SurfaceFrames pending classification. The front of the deque is probably not
// tracked by FrameTimeline and will never be presented. This will only result in a memory
@@ -2988,7 +2679,9 @@
void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
- handle->transformHint = mTransformHint;
+ handle->transformHint = mSkipReportingTransformHint
+ ? std::nullopt
+ : std::make_optional<uint32_t>(mTransformHint);
handle->dequeueReadyTime = dequeueReadyTime;
handle->currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
@@ -3193,7 +2886,6 @@
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.clientCacheId = bufferData.cachedBuffer;
-
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
? bufferData.acquireFence
: Fence::NO_FENCE;
@@ -3377,7 +3069,7 @@
return fenceSignaled;
}
-bool Layer::onPreComposition(nsecs_t refreshStartTime, bool /* updatingOutputGeometryThisFrame */) {
+bool Layer::onPreComposition(nsecs_t refreshStartTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
handle->refreshStartTime = refreshStartTime;
}
@@ -3492,7 +3184,6 @@
mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
mBufferInfo.mApi = mDrawingState.api;
mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
- mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(mDrawingState.clientCacheId);
}
Rect Layer::computeBufferCrop(const State& s) {
@@ -3511,7 +3202,6 @@
LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
- layer->mHwcSlotGenerator = mHwcSlotGenerator;
layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
return layer;
}
@@ -3633,7 +3323,7 @@
}
if (s.what & layer_state_t::eAlphaChanged) {
- if (mDrawingState.color.a != s.alpha) {
+ if (mDrawingState.color.a != s.color.a) {
ALOGV("%s: false [eAlphaChanged changed]", __func__);
return false;
}
@@ -3677,9 +3367,9 @@
}
}
- if (s.what & layer_state_t::eTransformChanged) {
- if (mDrawingState.bufferTransform != s.transform) {
- ALOGV("%s: false [eTransformChanged changed]", __func__);
+ if (s.what & layer_state_t::eBufferTransformChanged) {
+ if (mDrawingState.bufferTransform != s.bufferTransform) {
+ ALOGV("%s: false [eBufferTransformChanged changed]", __func__);
return false;
}
}
@@ -3788,26 +3478,29 @@
mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
}
-sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
// There's no need to get a CE Layer if the layer isn't going to draw anything.
- if (hasSomethingToDraw()) {
- return asLayerFE();
- } else {
- return nullptr;
- }
+ return hasSomethingToDraw() ? mLayerFE : nullptr;
}
-const Layer::LayerSnapshot* Layer::getLayerSnapshot() const {
+const LayerSnapshot* Layer::getLayerSnapshot() const {
return mSnapshot.get();
}
-Layer::LayerSnapshot* Layer::editLayerSnapshot() {
+LayerSnapshot* Layer::editLayerSnapshot() {
return mSnapshot.get();
}
+
const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
return mSnapshot.get();
}
+sp<LayerFE> Layer::copyCompositionEngineLayerFE() const {
+ auto result = mFlinger->getFactory().createLayerFE(mLayerFE->getDebugName());
+ result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot);
+ return result;
+}
+
void Layer::useSurfaceDamage() {
if (mFlinger->mForceFullDamage) {
surfaceDamageRegion = Region::INVALID_REGION;
@@ -3902,7 +3595,7 @@
}
if (display) {
- const Fps refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
+ const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
const std::optional<Fps> renderRate =
mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -4154,22 +3847,12 @@
return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
}
-void Layer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const {
- sp<GraphicBuffer> buffer = getBuffer();
- if (!buffer) {
- ALOGE("Buffer should not be null!");
- return;
- }
- GLConsumer::computeTransformMatrix(outMatrix, buffer->getWidth(), buffer->getHeight(),
- buffer->getPixelFormat(), mBufferInfo.mCrop,
- mBufferInfo.mTransform, filteringEnabled);
-}
-
void Layer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
mTransformHint = getFixedTransformHint();
if (mTransformHint == ui::Transform::ROT_INVALID) {
mTransformHint = displayTransformHint;
}
+ mSkipReportingTransformHint = false;
}
const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
@@ -4177,15 +3860,12 @@
}
bool Layer::setColor(const half3& color) {
- if (mDrawingState.color.r == color.r && mDrawingState.color.g == color.g &&
- mDrawingState.color.b == color.b) {
+ if (mDrawingState.color.rgb == color) {
return false;
}
mDrawingState.sequence++;
- mDrawingState.color.r = color.r;
- mDrawingState.color.g = color.g;
- mDrawingState.color.b = color.b;
+ mDrawingState.color.rgb = color;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -4243,9 +3923,17 @@
}
snapshot->bufferSize = getBufferSize(mDrawingState);
snapshot->externalTexture = mBufferInfo.mBuffer;
+ snapshot->hasReadyFrame = hasReadyFrame();
preparePerFrameCompositionState();
}
+void Layer::updateChildrenSnapshots(bool updateGeometry) {
+ for (const sp<Layer>& child : mDrawingChildren) {
+ child->updateSnapshot(updateGeometry);
+ child->updateChildrenSnapshots(updateGeometry);
+ }
+}
+
void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
mSnapshot->layerMetadata = parentMetadata;
mSnapshot->layerMetadata.merge(mDrawingState.metadata);
@@ -4278,6 +3966,28 @@
}
}
+LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
+ if (mLayer) {
+ mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
+ }
+}
+
+LayerSnapshotGuard::~LayerSnapshotGuard() {
+ if (mLayer) {
+ mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot);
+ }
+}
+
+LayerSnapshotGuard::LayerSnapshotGuard(LayerSnapshotGuard&& other) : mLayer(other.mLayer) {
+ other.mLayer = nullptr;
+}
+
+LayerSnapshotGuard& LayerSnapshotGuard::operator=(LayerSnapshotGuard&& other) {
+ mLayer = other.mLayer;
+ other.mLayer = nullptr;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4ff86e5..08a13a3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -51,7 +51,7 @@
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
-#include "HwcSlotGenerator.h"
+#include "LayerFE.h"
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
#include "SurfaceFlinger.h"
@@ -77,32 +77,12 @@
class LayerDebugInfo;
}
-namespace impl {
-class SurfaceInterceptor;
-}
-
namespace frametimeline {
class SurfaceFrame;
} // namespace frametimeline
-struct LayerCreationArgs {
- LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata);
-
- SurfaceFlinger* flinger;
- const sp<Client> client;
- std::string name;
- uint32_t flags;
- LayerMetadata metadata;
-
- pid_t callingPid;
- uid_t callingUid;
- uint32_t textureName;
- std::optional<uint32_t> sequence = std::nullopt;
- bool addToRoot = true;
-};
-
-class Layer : public virtual RefBase, compositionengine::LayerFE {
- static std::atomic<int32_t> sSequence;
+class Layer : public virtual RefBase {
+public:
// The following constants represent priority of the window. SF uses this information when
// deciding which window has a priority when deciding about the refresh rate of the screen.
// Priority 0 is considered the highest priority. -1 means that the priority is unset.
@@ -114,7 +94,6 @@
// Windows that are not in focus, but voted for a specific mode ID.
static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
-public:
enum { // flags for doTransaction()
eDontUpdateGeometryState = 0x00000001,
eVisibleRegion = 0x00000002,
@@ -133,43 +112,6 @@
inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
};
- struct RoundedCornerState {
- RoundedCornerState() = default;
- RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
- : cropRect(cropRect), radius(radius) {}
-
- // Rounded rectangle in local layer coordinate space.
- FloatRect cropRect = FloatRect();
- // Radius of the rounded rectangle.
- vec2 radius;
- bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
- };
-
- // LayerSnapshot stores Layer state used by Composition Engine and Render Engine. Composition
- // Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
- // passed to Render Engine are created using properties stored on this struct.
- //
- // TODO(b/238781169) Implement LayerFE as a separate subclass. Migrate LayerSnapshot to that
- // LayerFE subclass.
- struct LayerSnapshot : public compositionengine::LayerFECompositionState {
- int32_t sequence;
- std::string name;
- uint32_t textureName;
- bool contentOpaque;
- RoundedCornerState roundedCorner;
- StretchEffect stretchEffect;
- FloatRect transformedBounds;
- renderengine::ShadowSettings shadowSettings;
- bool premultipliedAlpha;
- bool isHdrY410;
- bool bufferNeedsFiltering;
- ui::Transform transform;
- Rect bufferSize;
- std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- LayerMetadata layerMetadata;
- LayerMetadata relativeLayerMetadata;
- };
-
using FrameRate = scheduler::LayerInfo::FrameRate;
using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
@@ -283,46 +225,6 @@
bool dimmingEnabled = true;
};
- /*
- * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
- * is called.
- */
- class LayerCleaner {
- sp<SurfaceFlinger> mFlinger;
- sp<Layer> mLayer;
- BBinder* mHandle;
-
- protected:
- ~LayerCleaner() {
- // destroy client resources
- mFlinger->onHandleDestroyed(mHandle, mLayer);
- }
-
- public:
- LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle)
- : mFlinger(flinger), mLayer(layer), mHandle(handle) {}
- };
-
- /*
- * The layer handle is just a BBinder object passed to the client
- * (remote process) -- we don't keep any reference on our side such that
- * the dtor is called when the remote side let go of its reference.
- *
- * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
- * this layer when the handle is destroyed.
- */
- class Handle : public BBinder, public LayerCleaner {
- public:
- Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : LayerCleaner(flinger, layer, this), owner(layer) {}
- const String16& getInterfaceDescriptor() const override { return kDescriptor; }
-
- static const String16 kDescriptor;
- wp<Layer> owner;
- };
-
- static wp<Layer> fromHandle(const sp<IBinder>& handle);
-
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
@@ -377,7 +279,9 @@
virtual bool setTrustedOverlay(bool);
virtual bool setFlags(uint32_t flags, uint32_t mask);
virtual bool setLayerStack(ui::LayerStack);
- virtual ui::LayerStack getLayerStack() const;
+ virtual ui::LayerStack getLayerStack(
+ LayerVector::StateSet state = LayerVector::StateSet::Drawing) const;
+
virtual bool setMetadata(const LayerMetadata& data);
virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -417,7 +321,8 @@
ui::Dataspace getDataSpace() const;
ui::Dataspace getRequestedDataSpace() const;
- virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
+ virtual sp<LayerFE> getCompositionEngineLayerFE() const;
+ virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
const LayerSnapshot* getLayerSnapshot() const;
LayerSnapshot* editLayerSnapshot();
@@ -561,7 +466,7 @@
// corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
- bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
+ bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); }
PixelFormat getPixelFormat() const;
/**
@@ -592,7 +497,6 @@
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
- int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
bool mFrameLatencyNeeded{false};
};
@@ -602,24 +506,15 @@
// implements compositionengine::LayerFE
const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
- // Called before composition. updatingOutputGeometryThisFrame is used by ARC++'s Layer subclass.
- bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame);
- std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const override;
+ bool onPreComposition(nsecs_t refreshStartTime);
void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
- void setWasClientComposed(const sp<Fence>& fence) override {
+ void setWasClientComposed(const sp<Fence>& fence) {
mLastClientCompositionFence = fence;
mClearClientCompositionFenceOnLayerDisplayed = false;
}
- const LayerMetadata* getMetadata() const override { return &mSnapshot->layerMetadata; }
-
- const LayerMetadata* getRelativeMetadata() const override {
- return &mSnapshot->relativeLayerMetadata;
- }
-
- const char* getDebugName() const override;
+ const char* getDebugName() const;
bool setShadowRadius(float shadowRadius);
@@ -644,7 +539,7 @@
// Compute bounds for the layer and cache the results.
void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
- int32_t getSequence() const override { return sequence; }
+ int32_t getSequence() const { return sequence; }
// For tracing.
// TODO: Replace with raw buffer id from buffer metadata when that becomes available.
@@ -727,7 +622,7 @@
* Sets display transform hint on BufferLayerConsumer.
*/
void updateTransformHint(ui::Transform::RotationFlags);
-
+ void skipReportingTransformHint();
inline const State& getDrawingState() const { return mDrawingState; }
inline State& getDrawingState() { return mDrawingState; }
@@ -735,7 +630,7 @@
void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
- void dumpCallingUidPid(std::string& result) const;
+ void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
@@ -881,7 +776,9 @@
bool mPendingHWCDestroy{false};
- bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
+ bool backpressureEnabled() const {
+ return mDrawingState.flags & layer_state_t::eEnableBackpressure;
+ }
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
@@ -904,7 +801,7 @@
// Updates the LayerSnapshot. This must be called prior to sending layer data to
// CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
- // Layer::prepareClientComposition).
+ // LayerFE::prepareClientComposition).
//
// TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
// CompositionEngine to create a single path for composing layers.
@@ -914,8 +811,6 @@
std::unordered_set<Layer*>& visited);
protected:
- friend class impl::SurfaceInterceptor;
-
// For unit tests
friend class TestableSurfaceFlinger;
friend class FpsReporterTest;
@@ -932,7 +827,6 @@
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
- sp<compositionengine::LayerFE> asLayerFE() const;
sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
bool isClone() { return mClonedFrom != nullptr; }
bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
@@ -945,12 +839,6 @@
void addChildToDrawing(const sp<Layer>&);
void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
- // the settings clears the content with a solid black fill.
- void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
- void prepareShadowClientComposition(LayerFE::LayerSettings& caster,
- const Rect& layerStackRect) const;
-
void prepareBasicGeometryCompositionState();
void prepareGeometryCompositionState();
void prepareCursorCompositionState();
@@ -1106,10 +994,6 @@
// Fills in the frame and transform info for the gui::WindowInfo.
void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
- // Computes the transform matrix using the setFilteringEnabled to determine whether the
- // transform matrix should be computed for use with bilinear filtering.
- void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) const;
-
inline void tracePendingBufferCount(int32_t pendingBuffers);
// Latch sideband stream and returns true if the dirty region should be updated.
@@ -1133,8 +1017,6 @@
const sp<Fence>& releaseFence,
uint32_t currentMaxAcquiredBufferCount);
- std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
// Returns true if there is a valid color to fill.
bool fillsColor() const;
// Returns true if this layer has a blur value.
@@ -1144,13 +1026,13 @@
return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr));
}
+ bool hasBufferOrSidebandStreamInDrawing() const {
+ return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr));
+ }
+
bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
- void prepareBufferStateClientComposition(
- compositionengine::LayerFE::LayerSettings&,
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
- void prepareEffectsClientComposition(
- compositionengine::LayerFE::LayerSettings&,
- compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+
+ void updateChildrenSnapshots(bool updateGeometry);
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
@@ -1170,11 +1052,6 @@
bool mGetHandleCalled = false;
- // Tracks the process and user id of the caller when creating this layer
- // to help debugging.
- pid_t mCallingPid;
- uid_t mCallingUid;
-
// The current layer is a clone of mClonedFrom. This means that this layer will update it's
// properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
@@ -1209,6 +1086,7 @@
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+ bool mSkipReportingTransformHint = true;
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
uint64_t mPreviousReleasedFrameNumber = 0;
@@ -1223,7 +1101,7 @@
std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
// An upper bound on the number of SurfaceFrames in the pending classifications deque.
- static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
+ static constexpr int kPendingClassificationMaxSurfaceFrames = 50;
const std::string mBlastTransactionName{"BufferTX - " + mName};
// This integer is incremented everytime a buffer arrives at the server for this layer,
@@ -1241,9 +1119,33 @@
// not specify a destination frame.
ui::Transform mRequestedTransform;
- sp<HwcSlotGenerator> mHwcSlotGenerator;
-
+ sp<LayerFE> mLayerFE;
std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
+
+ friend class LayerSnapshotGuard;
+};
+
+// LayerSnapshotGuard manages the movement of LayerSnapshot between a Layer and its corresponding
+// LayerFE. This class must be used whenever LayerFEs are passed to CompositionEngine. Instances of
+// LayerSnapshotGuard should only be constructed on the main thread and should not be moved outside
+// the main thread.
+//
+// Moving the snapshot instead of sharing common state prevents use of LayerFE outside the main
+// thread by making errors obvious (i.e. use outside the main thread results in SEGFAULTs due to
+// nullptr dereference).
+class LayerSnapshotGuard {
+public:
+ LayerSnapshotGuard(Layer* layer) REQUIRES(kMainThreadContext);
+ ~LayerSnapshotGuard() REQUIRES(kMainThreadContext);
+
+ LayerSnapshotGuard(const LayerSnapshotGuard&) = delete;
+ LayerSnapshotGuard& operator=(const LayerSnapshotGuard&) = delete;
+
+ LayerSnapshotGuard(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext);
+ LayerSnapshotGuard& operator=(LayerSnapshotGuard&& other) REQUIRES(kMainThreadContext);
+
+private:
+ Layer* mLayer;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
new file mode 100644
index 0000000..363adc6
--- /dev/null
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "LayerFE"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/GLConsumer.h>
+#include <gui/TraceUtils.h>
+#include <math/vec3.h>
+#include <system/window.h>
+#include <utils/Log.h>
+
+#include "DisplayDevice.h"
+#include "LayerFE.h"
+
+namespace android {
+
+namespace {
+constexpr float defaultMaxLuminance = 1000.0;
+
+constexpr mat4 inverseOrientation(uint32_t transform) {
+ const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ mat4 tr;
+
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ tr = tr * rot90;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ tr = tr * flipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ tr = tr * flipV;
+ }
+ return inverse(tr);
+}
+
+FloatRect reduce(const FloatRect& win, const Region& exclude) {
+ if (CC_LIKELY(exclude.isEmpty())) {
+ return win;
+ }
+ // Convert through Rect (by rounding) for lack of FloatRegion
+ return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
+}
+
+// Computes the transform matrix using the setFilteringEnabled to determine whether the
+// transform matrix should be computed for use with bilinear filtering.
+void getDrawingTransformMatrix(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ Rect bufferCrop, uint32_t bufferTransform, bool filteringEnabled,
+ float outMatrix[16]) {
+ if (!buffer) {
+ ALOGE("Buffer should not be null!");
+ return;
+ }
+ GLConsumer::computeTransformMatrix(outMatrix, static_cast<float>(buffer->getWidth()),
+ static_cast<float>(buffer->getHeight()),
+ buffer->getPixelFormat(), bufferCrop, bufferTransform,
+ filteringEnabled);
+}
+
+} // namespace
+
+LayerFE::LayerFE(const std::string& name) : mName(name) {}
+
+const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const {
+ return mSnapshot.get();
+}
+
+bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) {
+ mCompositionResult.refreshStartTime = refreshStartTime;
+ return mSnapshot->hasReadyFrame;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+ prepareClientCompositionInternal(targetSettings);
+ // Nothing to render.
+ if (!layerSettings) {
+ return {};
+ }
+
+ // HWC requests to clear this layer.
+ if (targetSettings.clearContent) {
+ prepareClearClientComposition(*layerSettings, false /* blackout */);
+ return layerSettings;
+ }
+
+ // set the shadow for the layer if needed
+ prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+
+ return layerSettings;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal(
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ ATRACE_CALL();
+ compositionengine::LayerFE::LayerSettings layerSettings;
+ layerSettings.geometry.boundaries =
+ reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint);
+ layerSettings.geometry.positionTransform = mSnapshot->geomLayerTransform.asMatrix4();
+
+ // skip drawing content if the targetSettings indicate the content will be occluded
+ const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
+ layerSettings.skipContentDraw = !drawContent;
+
+ if (!mSnapshot->colorTransformIsIdentity) {
+ layerSettings.colorTransform = mSnapshot->colorTransform;
+ }
+
+ const auto& roundedCornerState = mSnapshot->roundedCorner;
+ layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
+ layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+ layerSettings.alpha = mSnapshot->alpha;
+ layerSettings.sourceDataspace = mSnapshot->dataspace;
+
+ // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
+ // We do this here instead of in buffer info so that dumpsys can still report layers that are
+ // using the 170M transfer.
+ if (targetSettings.treat170mAsSrgb &&
+ (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
+ HAL_DATASPACE_TRANSFER_SMPTE_170M) {
+ layerSettings.sourceDataspace = static_cast<ui::Dataspace>(
+ (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) |
+ (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) |
+ HAL_DATASPACE_TRANSFER_SRGB);
+ }
+
+ layerSettings.whitePointNits = targetSettings.whitePointNits;
+ switch (targetSettings.blurSetting) {
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
+ layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius;
+ layerSettings.blurRegions = mSnapshot->blurRegions;
+ layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
+ layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius;
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
+ layerSettings.blurRegions = mSnapshot->blurRegions;
+ layerSettings.blurRegionTransform = mSnapshot->blurRegionTransform.asMatrix4();
+ break;
+ case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
+ default:
+ break;
+ }
+ layerSettings.stretchEffect = mSnapshot->stretchEffect;
+ // Record the name of the layer for debugging further down the stack.
+ layerSettings.name = mSnapshot->name;
+
+ if (hasEffect() && !hasBufferOrSidebandStream()) {
+ prepareEffectsClientComposition(layerSettings, targetSettings);
+ return layerSettings;
+ }
+
+ prepareBufferStateClientComposition(layerSettings, targetSettings);
+ return layerSettings;
+}
+
+void LayerFE::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
+ bool blackout) const {
+ layerSettings.source.buffer.buffer = nullptr;
+ layerSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+ layerSettings.disableBlending = true;
+ layerSettings.bufferId = 0;
+ layerSettings.frameNumber = 0;
+
+ // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
+ layerSettings.alpha = blackout ? 1.0f : 0.0f;
+ layerSettings.name = mSnapshot->name;
+}
+
+void LayerFE::prepareEffectsClientComposition(
+ compositionengine::LayerFE::LayerSettings& layerSettings,
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+ if (targetSettings.realContentIsVisible && fillsColor()) {
+ // Set color for color fill settings.
+ layerSettings.source.solidColor = mSnapshot->color.rgb;
+ } else if (hasBlur() || drawShadows()) {
+ layerSettings.skipContentDraw = true;
+ }
+}
+
+void LayerFE::prepareBufferStateClientComposition(
+ compositionengine::LayerFE::LayerSettings& layerSettings,
+ compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+ ATRACE_CALL();
+ if (CC_UNLIKELY(!mSnapshot->externalTexture)) {
+ // If there is no buffer for the layer or we have sidebandstream where there is no
+ // activeBuffer, then we need to return LayerSettings.
+ return;
+ }
+ const bool blackOutLayer =
+ (mSnapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
+ ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) && !targetSettings.isSecure);
+ const bool bufferCanBeUsedAsHwTexture =
+ mSnapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+ if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+ ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+ mSnapshot->name.c_str());
+ prepareClearClientComposition(layerSettings, true /* blackout */);
+ return;
+ }
+
+ layerSettings.source.buffer.buffer = mSnapshot->externalTexture;
+ layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque;
+ layerSettings.source.buffer.fence = mSnapshot->acquireFence;
+ layerSettings.source.buffer.textureName = mSnapshot->textureName;
+ layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha;
+ layerSettings.source.buffer.isY410BT2020 = mSnapshot->isHdrY410;
+ bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ float maxLuminance = 0.f;
+ if (hasSmpte2086 && hasCta861_3) {
+ maxLuminance = std::min(mSnapshot->hdrMetadata.smpte2086.maxLuminance,
+ mSnapshot->hdrMetadata.cta8613.maxContentLightLevel);
+ } else if (hasSmpte2086) {
+ maxLuminance = mSnapshot->hdrMetadata.smpte2086.maxLuminance;
+ } else if (hasCta861_3) {
+ maxLuminance = mSnapshot->hdrMetadata.cta8613.maxContentLightLevel;
+ } else {
+ switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // Behavior-match previous releases for HDR content
+ maxLuminance = defaultMaxLuminance;
+ break;
+ }
+ }
+ layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
+ layerSettings.frameNumber = mSnapshot->frameNumber;
+ layerSettings.bufferId = mSnapshot->externalTexture->getId();
+
+ const bool useFiltering = targetSettings.needsFiltering ||
+ mSnapshot->geomLayerTransform.needsBilinearFiltering() ||
+ mSnapshot->bufferNeedsFiltering;
+
+ // Query the texture matrix given our current filtering mode.
+ float textureMatrix[16];
+ getDrawingTransformMatrix(layerSettings.source.buffer.buffer, mSnapshot->geomContentCrop,
+ mSnapshot->geomBufferTransform, useFiltering, textureMatrix);
+
+ if (mSnapshot->geomBufferUsesDisplayInverseTransform) {
+ /*
+ * the code below applies the primary display's inverse transform to
+ * the texture transform
+ */
+ uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ mat4 tr = inverseOrientation(transform);
+
+ /**
+ * TODO(b/36727915): This is basically a hack.
+ *
+ * Ensure that regardless of the parent transformation,
+ * this buffer is always transformed from native display
+ * orientation to display orientation. For example, in the case
+ * of a camera where the buffer remains in native orientation,
+ * we want the pixels to always be upright.
+ */
+ const auto parentTransform = mSnapshot->transform;
+ tr = tr * inverseOrientation(parentTransform.getOrientation());
+
+ // and finally apply it to the original texture matrix
+ const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+ memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+ }
+
+ const Rect win{layerSettings.geometry.boundaries};
+ float bufferWidth = static_cast<float>(mSnapshot->bufferSize.getWidth());
+ float bufferHeight = static_cast<float>(mSnapshot->bufferSize.getHeight());
+
+ // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+ // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+ // ignore them.
+ if (!mSnapshot->bufferSize.isValid()) {
+ bufferWidth = float(win.right) - float(win.left);
+ bufferHeight = float(win.bottom) - float(win.top);
+ }
+
+ const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+ const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+ const float translateY = float(win.top) / bufferHeight;
+ const float translateX = float(win.left) / bufferWidth;
+
+ // Flip y-coordinates because GLConsumer expects OpenGL convention.
+ mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
+ mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
+ mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
+ mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
+
+ layerSettings.source.buffer.useTextureFiltering = useFiltering;
+ layerSettings.source.buffer.textureTransform =
+ mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+ return;
+}
+
+void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+ const Rect& layerStackRect) const {
+ renderengine::ShadowSettings state = mSnapshot->shadowSettings;
+ if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
+ return;
+ }
+
+ // Shift the spot light x-position to the middle of the display and then
+ // offset it by casting layer's screen pos.
+ state.lightPos.x =
+ (static_cast<float>(layerStackRect.width()) / 2.f) - mSnapshot->transformedBounds.left;
+ state.lightPos.y -= mSnapshot->transformedBounds.top;
+ caster.shadow = state;
+}
+
+void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
+ mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult));
+}
+
+CompositionResult&& LayerFE::stealCompositionResult() {
+ return std::move(mCompositionResult);
+}
+
+const char* LayerFE::getDebugName() const {
+ return mName.c_str();
+}
+
+const LayerMetadata* LayerFE::getMetadata() const {
+ return &mSnapshot->layerMetadata;
+}
+
+const LayerMetadata* LayerFE::getRelativeMetadata() const {
+ return &mSnapshot->relativeLayerMetadata;
+}
+
+int32_t LayerFE::getSequence() const {
+ return mSnapshot->sequence;
+}
+
+bool LayerFE::hasRoundedCorners() const {
+ return mSnapshot->roundedCorner.hasRoundedCorners();
+}
+
+void LayerFE::setWasClientComposed(const sp<Fence>& fence) {
+ mCompositionResult.lastClientCompositionFence = fence;
+}
+
+bool LayerFE::hasBufferOrSidebandStream() const {
+ return mSnapshot->externalTexture || mSnapshot->sidebandStream;
+}
+
+bool LayerFE::fillsColor() const {
+ return mSnapshot->color.r >= 0.0_hf && mSnapshot->color.g >= 0.0_hf &&
+ mSnapshot->color.b >= 0.0_hf;
+}
+
+bool LayerFE::hasBlur() const {
+ return mSnapshot->backgroundBlurRadius > 0 || mSnapshot->blurRegions.size() > 0;
+}
+
+bool LayerFE::drawShadows() const {
+ return mSnapshot->shadowSettings.length > 0.f &&
+ (mSnapshot->shadowSettings.ambientColor.a > 0 ||
+ mSnapshot->shadowSettings.spotColor.a > 0);
+};
+
+const sp<GraphicBuffer> LayerFE::getBuffer() const {
+ return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
new file mode 100644
index 0000000..822bcb7
--- /dev/null
+++ b/services/surfaceflinger/LayerFE.h
@@ -0,0 +1,119 @@
+/*
+ * 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 <gui/LayerMetadata.h>
+
+#include "compositionengine/LayerFE.h"
+#include "compositionengine/LayerFECompositionState.h"
+#include "renderengine/LayerSettings.h"
+
+namespace android {
+struct RoundedCornerState {
+ RoundedCornerState() = default;
+ RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
+ : cropRect(cropRect), radius(radius) {}
+
+ // Rounded rectangle in local layer coordinate space.
+ FloatRect cropRect = FloatRect();
+ // Radius of the rounded rectangle.
+ vec2 radius;
+ bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
+};
+
+// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
+// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
+// passed to Render Engine are created using properties stored on this struct.
+struct LayerSnapshot : public compositionengine::LayerFECompositionState {
+ int32_t sequence;
+ std::string name;
+ uint32_t textureName;
+ bool contentOpaque;
+ RoundedCornerState roundedCorner;
+ StretchEffect stretchEffect;
+ FloatRect transformedBounds;
+ renderengine::ShadowSettings shadowSettings;
+ bool premultipliedAlpha;
+ bool isHdrY410;
+ bool bufferNeedsFiltering;
+ ui::Transform transform;
+ Rect bufferSize;
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+ gui::LayerMetadata layerMetadata;
+ gui::LayerMetadata relativeLayerMetadata;
+ bool contentDirty;
+ bool hasReadyFrame;
+ ui::Transform blurRegionTransform;
+};
+
+struct CompositionResult {
+ // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
+ // and remove this field.
+ nsecs_t refreshStartTime = 0;
+ std::vector<ftl::SharedFuture<FenceResult>> releaseFences;
+ sp<Fence> lastClientCompositionFence = nullptr;
+};
+
+class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
+public:
+ LayerFE(const std::string& name);
+
+ // compositionengine::LayerFE overrides
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
+ bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override;
+ void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
+ const char* getDebugName() const override;
+ int32_t getSequence() const override;
+ bool hasRoundedCorners() const override;
+ void setWasClientComposed(const sp<Fence>&) override;
+ const gui::LayerMetadata* getMetadata() const override;
+ const gui::LayerMetadata* getRelativeMetadata() const override;
+ std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+ CompositionResult&& stealCompositionResult();
+
+ std::unique_ptr<LayerSnapshot> mSnapshot;
+
+private:
+ std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+ // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+ // the settings clears the content with a solid black fill.
+ void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
+ void prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+ const Rect& layerStackRect) const;
+ void prepareBufferStateClientComposition(
+ compositionengine::LayerFE::LayerSettings&,
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+ void prepareEffectsClientComposition(
+ compositionengine::LayerFE::LayerSettings&,
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+
+ bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
+ bool hasBufferOrSidebandStream() const;
+
+ bool fillsColor() const;
+ bool hasBlur() const;
+ bool drawShadows() const;
+
+ const sp<GraphicBuffer> getBuffer() const;
+
+ CompositionResult mCompositionResult;
+ std::string mName;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 6bc7dc1..554fae4 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -18,6 +18,7 @@
#include <ui/Transform.h>
#include "DisplayDevice.h"
+#include "FrontEnd/LayerCreationArgs.h"
#include "Layer.h"
#include "LayerRenderArea.h"
#include "SurfaceFlinger.h"
@@ -30,6 +31,7 @@
// Compute and cache the bounds for the new parent layer.
newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
0.f /* shadowRadius */);
+ newParent->updateSnapshot(true /* updateGeometry */);
oldParent->setChildrenDrawingParent(newParent);
};
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9b19afb..7aa7e17 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -42,8 +42,8 @@
constexpr int kDigitHeight = 100;
constexpr int kDigitSpace = 16;
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
constexpr int kBufferHeight = kDigitHeight;
SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
@@ -121,16 +121,10 @@
drawSegment(Segment::Bottom, left, color, canvas);
}
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
ui::Transform::RotationFlags rotation,
- bool showSpinner) -> Buffers {
- if (number < 0 || number > 1000) return {};
-
- const auto hundreds = number / 100;
- const auto tens = (number / 10) % 10;
- const auto ones = number % 10;
-
- const size_t loopCount = showSpinner ? 6 : 1;
+ ftl::Flags<Features> features) -> Buffers {
+ const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
Buffers buffers;
buffers.reserve(loopCount);
@@ -169,20 +163,9 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- if (tens != 0) {
- drawDigit(tens, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- drawDigit(ones, left, color, *canvas);
- left += kDigitWidth + kDigitSpace;
-
- if (showSpinner) {
+ drawNumber(displayFps, left, color, *canvas);
+ left += 3 * (kDigitWidth + kDigitSpace);
+ if (features.test(Features::Spinner)) {
switch (i) {
case 0:
drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +188,13 @@
}
}
+ left += kDigitWidth + kDigitSpace;
+
+ if (features.test(Features::RenderRate)) {
+ drawNumber(renderFps, left, color, *canvas);
+ }
+ left += 3 * (kDigitWidth + kDigitSpace);
+
void* pixels = nullptr;
buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
@@ -219,6 +209,23 @@
return buffers;
}
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+ SkCanvas& canvas) {
+ if (number < 0 || number >= 1000) return;
+
+ if (number >= 100) {
+ drawDigit(number / 100, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ if (number >= 10) {
+ drawDigit((number / 10) % 10, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ drawDigit(number % 10, left, color, canvas);
+}
+
std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
sp<SurfaceControl> surfaceControl =
SurfaceComposerClient::getDefault()
@@ -228,10 +235,8 @@
return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
}
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
- : mFpsRange(fpsRange),
- mShowSpinner(showSpinner),
- mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+ : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
@@ -243,10 +248,15 @@
.apply();
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
+ // avoid caching different render rates if RenderRate is anyway not visible
+ if (!mFeatures.test(Features::RenderRate)) {
+ renderFps = 0_Hz;
+ }
+
const auto transformHint =
static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
@@ -266,17 +276,20 @@
.setTransform(mSurfaceControl->get(), transform)
.apply();
- BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+ BufferCache::const_iterator it =
+ mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
const int minFps = mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
- // Clamp to the range. The current fps may be outside of this range if the display has
- // changed its set of supported refresh rates.
- const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+ // Clamp to the range. The current displayFps may be outside of this range if the display
+ // has changed its set of supported refresh rates.
+ const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+ const int renderIntFps = renderFps.getIntValue();
// Ensure non-zero range to avoid division by zero.
- const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+ const float fpsScale =
+ static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
constexpr SkColor kMinFpsColor = SK_ColorRED;
constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +305,11 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
- it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+ auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+ mFeatures);
+ it = mBufferCache
+ .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+ .first;
}
return it->second;
@@ -303,7 +319,7 @@
constexpr int32_t kMaxWidth = 1000;
const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
const auto height = 2 * width;
- Rect frame((3 * width) >> 4, height >> 5);
+ Rect frame((5 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
createTransaction(mSurfaceControl->get())
@@ -317,16 +333,17 @@
createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
- mCurrentFps = fps;
- const auto buffer = getOrCreateBuffers(fps)[mFrame];
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+ mDisplayFps = displayFps;
+ mRenderFps = renderFps;
+ const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
- if (!mShowSpinner || !mCurrentFps) return;
+ if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
- const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+ const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..d6f828f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,6 +19,7 @@
#include <SkColor.h>
#include <vector>
+#include <ftl/flags.h>
#include <ftl/small_map.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
@@ -50,11 +51,16 @@
class RefreshRateOverlay {
public:
- RefreshRateOverlay(FpsRange, bool showSpinner);
+ enum class Features {
+ Spinner = 1 << 0,
+ RenderRate = 1 << 1,
+ };
+
+ RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
- void changeRefreshRate(Fps);
+ void changeRefreshRate(Fps, Fps);
void animate();
private:
@@ -62,32 +68,39 @@
class SevenSegmentDrawer {
public:
- static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+ static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+ ftl::Flags<Features>);
private:
enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
static void drawSegment(Segment, int left, SkColor, SkCanvas&);
static void drawDigit(int digit, int left, SkColor, SkCanvas&);
+ static void drawNumber(int number, int left, SkColor, SkCanvas&);
};
- const Buffers& getOrCreateBuffers(Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps);
struct Key {
- int fps;
+ int displayFps;
+ int renderFps;
ui::Transform::RotationFlags flags;
- bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+ bool operator==(Key other) const {
+ return displayFps == other.displayFps && renderFps == other.renderFps &&
+ flags == other.flags;
+ }
};
using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
BufferCache mBufferCache;
- std::optional<Fps> mCurrentFps;
+ std::optional<Fps> mDisplayFps;
+ std::optional<Fps> mRenderFps;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
- const bool mShowSpinner;
+ const ftl::Flags<Features> mFeatures;
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 8dd3b0f..6e64e0a 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -40,6 +40,7 @@
#include "DisplayDevice.h"
#include "DisplayRenderArea.h"
+#include "FrontEnd/LayerCreationArgs.h"
#include "Layer.h"
#include "Scheduler/VsyncController.h"
#include "SurfaceFlinger.h"
@@ -129,12 +130,12 @@
}
}
-void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
+void RegionSamplingThread::addListener(const Rect& samplingArea, uint32_t stopLayerId,
const sp<IRegionSamplingListener>& listener) {
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
std::lock_guard lock(mSamplingMutex);
- mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
+ mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayerId, listener});
}
void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
@@ -291,8 +292,8 @@
if (stopLayerFound) return;
// Likewise if we just found a stop layer, set the flag and abort
- for (const auto& [area, stopLayer, listener] : descriptors) {
- if (layer == stopLayer.promote().get()) {
+ for (const auto& [area, stopLayerId, listener] : descriptors) {
+ if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) {
stopLayerFound = true;
return;
}
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 686b4b1..b62b15c 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -26,6 +26,7 @@
#include <chrono>
#include <condition_variable>
+#include <cstdint>
#include <mutex>
#include <thread>
#include <unordered_map>
@@ -73,7 +74,7 @@
// Add a listener to receive luma notifications. The luma reported via listener will
// report the median luma for the layers under the stopLayerHandle, in the samplingArea region.
- void addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
+ void addListener(const Rect& samplingArea, uint32_t stopLayerId,
const sp<IRegionSamplingListener>& listener);
// Remove the listener to stop receiving median luma notifications.
void removeListener(const sp<IRegionSamplingListener>& listener);
@@ -87,7 +88,7 @@
private:
struct Descriptor {
Rect area = Rect::EMPTY_RECT;
- wp<Layer> stopLayer;
+ uint32_t stopLayerId;
sp<IRegionSamplingListener> listener;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index d3f53c1..008d8c4 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -124,12 +124,12 @@
return event;
}
-DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
+DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
- systemTime()};
- event.modeChange.modeId = mode->getId().value();
- event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ mode.modePtr->getPhysicalDisplayId(), systemTime()};
+ event.modeChange.modeId = mode.modePtr->getId().value();
+ event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
return event;
}
@@ -237,16 +237,13 @@
EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
android::frametimeline::TokenManager* tokenManager,
- InterceptVSyncsCallback interceptVSyncsCallback,
ThrottleVsyncCallback throttleVsyncCallback,
GetVsyncPeriodFunction getVsyncPeriodFunction)
: mVSyncSource(std::move(vsyncSource)),
mTokenManager(tokenManager),
- mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),
mThreadName(mVSyncSource->getName()) {
-
LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
"getVsyncPeriodFunction must not be null");
@@ -408,7 +405,7 @@
mCondition.notify_all();
}
-void EventThread::onModeChanged(DisplayModePtr mode) {
+void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeModeChanged(mode));
@@ -443,21 +440,13 @@
event = mPendingEvents.front();
mPendingEvents.pop_front();
- switch (event->header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- if (event->hotplug.connected && !mVSyncState) {
- mVSyncState.emplace(event->header.displayId);
- } else if (!event->hotplug.connected && mVSyncState &&
- mVSyncState->displayId == event->header.displayId) {
- mVSyncState.reset();
- }
- break;
-
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
- if (mInterceptVSyncsCallback) {
- mInterceptVSyncsCallback(event->header.timestamp);
- }
- break;
+ if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+ if (event->hotplug.connected && !mVSyncState) {
+ mVSyncState.emplace(event->header.displayId);
+ } else if (!event->hotplug.connected && mVSyncState &&
+ mVSyncState->displayId == event->header.displayId) {
+ mVSyncState.reset();
+ }
}
}
@@ -679,6 +668,7 @@
StringAppendF(&result, " %s\n", toString(*connection).c_str());
}
}
+ result += '\n';
}
const char* EventThread::toCString(State state) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index d85d140..43c3598 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
+#include <scheduler/FrameRateMode.h>
#include <condition_variable>
#include <cstdint>
#include <deque>
@@ -134,7 +135,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active mode and apps needs to be notified about the change
- virtual void onModeChanged(DisplayModePtr) = 0;
+ virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
// called when SF updates the Frame Rate Override list
virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -161,12 +162,11 @@
class EventThread : public android::EventThread, private VSyncSource::Callback {
public:
- using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
- EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
- ThrottleVsyncCallback, GetVsyncPeriodFunction);
+ EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, ThrottleVsyncCallback,
+ GetVsyncPeriodFunction);
~EventThread();
sp<EventThreadConnection> createEventConnection(
@@ -186,7 +186,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onModeChanged(DisplayModePtr) override;
+ void onModeChanged(const scheduler::FrameRateMode&) override;
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
@@ -225,7 +225,6 @@
const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
- const InterceptVSyncsCallback mInterceptVSyncsCallback;
const ThrottleVsyncCallback mThrottleVsyncCallback;
const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
index c233455..cb9bfe9 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -54,10 +54,9 @@
std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides(
bool supportsFrameRateOverrideByContent) {
std::lock_guard lock(mFrameRateOverridesLock);
+
std::vector<FrameRateOverride> overrides;
- overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(),
- mFrameRateOverridesFromBackdoor.size(),
- mFrameRateOverridesByContent.size()}));
+ overrides.reserve(maxOverridesCount());
for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
@@ -83,28 +82,34 @@
return overrides;
}
-void FrameRateOverrideMappings::dump(std::string& result) const {
- using base::StringAppendF;
+void FrameRateOverrideMappings::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
std::lock_guard lock(mFrameRateOverridesLock);
- StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
- }
- StringAppendF(&result, "}\n");
+ const bool hasOverrides = maxOverridesCount() > 0;
+ dumper.dump("FrameRateOverrides"sv, hasOverrides ? ""sv : "none"sv);
- StringAppendF(&result, "Frame Rate Overrides (GameManager): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
- }
- StringAppendF(&result, "}\n");
+ if (!hasOverrides) return;
- StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
- for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
- StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent);
+ dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+ dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor);
+}
+
+void FrameRateOverrideMappings::dump(utils::Dumper& dumper, std::string_view name,
+ const UidToFrameRateOverride& overrides) const {
+ if (overrides.empty()) return;
+
+ utils::Dumper::Indent indent(dumper);
+ dumper.dump(name);
+ {
+ utils::Dumper::Indent indent(dumper);
+ for (const auto& [uid, frameRate] : overrides) {
+ using namespace std::string_view_literals;
+ dumper.dump("(uid, frameRate)"sv, uid, frameRate);
+ }
}
- StringAppendF(&result, "}\n");
}
bool FrameRateOverrideMappings::updateFrameRateOverridesByContent(
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
index 4185a4c..da0f276 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
@@ -23,7 +23,10 @@
#include <map>
#include <optional>
+#include "Utils/Dumper.h"
+
namespace android::scheduler {
+
class FrameRateOverrideMappings {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
using UidToFrameRateOverride = std::map<uid_t, Fps>;
@@ -34,7 +37,6 @@
EXCLUDES(mFrameRateOverridesLock);
std::vector<FrameRateOverride> getAllFrameRateOverrides(bool supportsFrameRateOverrideByContent)
EXCLUDES(mFrameRateOverridesLock);
- void dump(std::string& result) const;
bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides)
EXCLUDES(mFrameRateOverridesLock);
void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride)
@@ -42,7 +44,17 @@
void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride)
EXCLUDES(mFrameRateOverridesLock);
+ void dump(utils::Dumper&) const;
+
private:
+ size_t maxOverridesCount() const REQUIRES(mFrameRateOverridesLock) {
+ return std::max({mFrameRateOverridesByContent.size(),
+ mFrameRateOverridesFromGameManager.size(),
+ mFrameRateOverridesFromBackdoor.size()});
+ }
+
+ void dump(utils::Dumper&, std::string_view name, const UidToFrameRateOverride&) const;
+
// The frame rate override lists need their own mutex as they are being read
// by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
mutable std::mutex mFrameRateOverridesLock;
@@ -53,4 +65,5 @@
UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock);
UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock);
};
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
deleted file mode 100644
index 760a4ee..0000000
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2018 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 <mutex>
-
-#include "EventThread.h"
-
-namespace android {
-
-/**
- * VSync signals used during SurfaceFlinger trace playback (traces we captured
- * with SurfaceInterceptor).
- */
-class InjectVSyncSource final : public VSyncSource {
-public:
- ~InjectVSyncSource() override = default;
-
- void setCallback(VSyncSource::Callback* callback) override {
- std::lock_guard<std::mutex> lock(mCallbackMutex);
- mCallback = callback;
- }
-
- void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
- nsecs_t deadlineTimestamp) {
- std::lock_guard<std::mutex> lock(mCallbackMutex);
- if (mCallback) {
- mCallback->onVSyncEvent(when, {expectedVSyncTimestamp, deadlineTimestamp});
- }
- }
-
- const char* getName() const override { return "inject"; }
- void setVSyncEnabled(bool) override {}
- void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {}
- VSyncData getLatestVSyncData() const override { return {}; }
- void dump(std::string&) const override {}
-
-private:
- std::mutex mCallbackMutex;
- VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index ae111c3..7c9cedfa 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -164,7 +164,7 @@
getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
}
-auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary {
+auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
Summary summary;
std::lock_guard lock(mLock);
@@ -178,7 +178,7 @@
ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
layerFocused ? "" : "not");
- const auto vote = info->getRefreshRateVote(configs, now);
+ const auto vote = info->getRefreshRateVote(selector, now);
// Skip NoVote layer as those don't have any requirements
if (vote.type == LayerVoteType::NoVote) {
continue;
@@ -275,7 +275,7 @@
std::string LayerHistory::dump() const {
std::lock_guard lock(mLock);
- return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+ return base::StringPrintf("{size=%zu, active=%zu}",
mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
mActiveLayerInfos.size());
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 12bec8d..5022906 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -27,7 +27,7 @@
#include <utility>
#include <vector>
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
namespace android {
@@ -39,7 +39,7 @@
class LayerHistory {
public:
- using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+ using LayerVoteType = RefreshRateSelector::LayerVoteType;
LayerHistory();
~LayerHistory();
@@ -67,10 +67,10 @@
// does not set a preference for refresh rate.
void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
- using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
+ using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- Summary summarize(const RefreshRateConfigs&, nsecs_t now);
+ Summary summarize(const RefreshRateSelector&, nsecs_t now);
void clear();
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 943615c..7247e4b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -187,8 +187,8 @@
return static_cast<nsecs_t>(averageFrameTime);
}
-std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(
- const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) {
+std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector,
+ nsecs_t now) {
static constexpr float MARGIN = 1.0f; // 1Hz
if (!hasEnoughDataForHeuristic()) {
ALOGV("Not enough data");
@@ -199,7 +199,7 @@
const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
if (refreshRateConsistent) {
- const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
+ const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate);
using fps_approx_ops::operator!=;
// To avoid oscillation, use the last calculated refresh rate if it is close enough.
@@ -222,7 +222,7 @@
: std::nullopt;
}
-LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs,
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
nsecs_t now) {
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
@@ -250,7 +250,7 @@
clearHistory(now);
}
- auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now);
+ auto refreshRate = calculateRefreshRateIfPossible(selector, now);
if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 28cb24a..a5ffbbe 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -28,7 +28,7 @@
#include <scheduler/Seamlessness.h>
#include "LayerHistory.h"
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
namespace android {
@@ -162,7 +162,7 @@
uid_t getOwnerUid() const { return mOwnerUid; }
- LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now);
+ LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
@@ -261,7 +261,7 @@
bool isFrequent(nsecs_t now) const;
bool isAnimating(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
- std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now);
+ std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateSelector&, nsecs_t now);
std::optional<nsecs_t> calculateAverageFrameTime() const;
bool isFrameTimeValid(const FrameTimeData&) const;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index e4e65b4..ae10ff4 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -57,37 +57,6 @@
mLooper(sp<Looper>::make(kAllowNonCallbacks)),
mHandler(std::move(handler)) {}
-// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
-// and remove the EventThread from MessageQueue
-void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
- auto& tube = mInjector.tube;
-
- if (const int fd = tube.getFd(); fd >= 0) {
- mLooper->removeFd(fd);
- }
-
- if (connection) {
- // The EventThreadConnection is retained when disabling injection, so avoid subsequently
- // stealing invalid FDs. Note that the stolen FDs are kept open.
- if (tube.getFd() < 0) {
- connection->stealReceiveChannel(&tube);
- } else {
- ALOGW("Recycling channel for VSYNC injection.");
- }
-
- mLooper->addFd(
- tube.getFd(), 0, Looper::EVENT_INPUT,
- [](int, int, void* data) {
- reinterpret_cast<MessageQueue*>(data)->injectorCallback();
- return 1; // Keep registration.
- },
- this);
- }
-
- std::lock_guard lock(mInjector.mutex);
- mInjector.connection = std::move(connection);
-}
-
void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
ATRACE_CALL();
// Trace VSYNC-sf
@@ -174,15 +143,6 @@
void MessageQueue::scheduleFrame() {
ATRACE_CALL();
- {
- std::lock_guard lock(mInjector.mutex);
- if (CC_UNLIKELY(mInjector.connection)) {
- ALOGD("%s while injecting VSYNC", __func__);
- mInjector.connection->requestNextVsync();
- return;
- }
- }
-
std::lock_guard lock(mVsync.mutex);
mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
@@ -190,22 +150,6 @@
.earliestVsync = mVsync.lastCallbackTime.ns()});
}
-void MessageQueue::injectorCallback() {
- ssize_t n;
- DisplayEventReceiver::Event buffer[8];
- while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
- for (int i = 0; i < n; i++) {
- if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- auto& vsync = buffer[i].vsync.vsyncData;
- mHandler->dispatchFrame(VsyncId{vsync.preferredVsyncId()},
- TimePoint::fromNs(
- vsync.preferredExpectedPresentationTime()));
- break;
- }
- }
- }
-}
-
auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
if (mHandler->isFramePending()) {
return Clock::now();
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 899233a..04de492 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -76,7 +76,6 @@
virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) = 0;
virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
- virtual void setInjector(sp<EventThreadConnection>) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void scheduleConfigure() = 0;
@@ -132,16 +131,7 @@
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
- struct Injector {
- gui::BitTube tube;
- std::mutex mutex;
- sp<EventThreadConnection> connection GUARDED_BY(mutex);
- };
-
Vsync mVsync;
- Injector mInjector;
-
- void injectorCallback();
public:
explicit MessageQueue(ICompositor&);
@@ -149,7 +139,6 @@
void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) override;
void setDuration(std::chrono::nanoseconds workDuration) override;
- void setInjector(sp<EventThreadConnection>) override;
void waitMessage() override;
void postMessage(sp<MessageHandler>&&) override;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 3c8dc64..cd45bfd 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -179,11 +179,5 @@
}
}
-std::string OneShotTimer::dump() const {
- std::ostringstream stream;
- stream << mInterval.count() << " ms";
- return stream.str();
-}
-
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 2017c31..f95646c 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -23,6 +23,7 @@
#include "../Clock.h"
#include <android-base/thread_annotations.h>
+#include <scheduler/Time.h>
namespace android {
namespace scheduler {
@@ -42,6 +43,8 @@
std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
+ Duration interval() const { return mInterval; }
+
// Initializes and turns on the idle timer.
void start();
// Stops the idle timer and any held resources.
@@ -49,8 +52,6 @@
// Resets the wakeup time and fires the reset callback.
void reset();
- std::string dump() const;
-
private:
// Enum to track in what state is the timer.
enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
deleted file mode 100644
index 3cb052c..0000000
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ /dev/null
@@ -1,1090 +0,0 @@
-/*
- * Copyright 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.
- */
-
-// #define LOG_NDEBUG 0
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <chrono>
-#include <cmath>
-
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <ftl/enum.h>
-#include <ftl/fake_guard.h>
-#include <utils/Trace.h>
-
-#include "../SurfaceFlingerProperties.h"
-#include "RefreshRateConfigs.h"
-
-#undef LOG_TAG
-#define LOG_TAG "RefreshRateConfigs"
-
-namespace android::scheduler {
-namespace {
-
-struct RefreshRateScore {
- DisplayModeIterator modeIt;
- float overallScore;
- struct {
- float modeBelowThreshold;
- float modeAboveThreshold;
- } fixedRateBelowThresholdLayersScore;
-};
-
-constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
-
-std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
- return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(),
- ftl::enum_string(layer.vote).c_str(), weight,
- ftl::enum_string(layer.seamlessness).c_str(),
- to_string(layer.desiredRefreshRate).c_str());
-}
-
-std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
- std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
- knownFrameRates.reserve(knownFrameRates.size() + modes.size());
-
- // Add all supported refresh rates.
- for (const auto& [id, mode] : modes) {
- knownFrameRates.push_back(mode->getFps());
- }
-
- // Sort and remove duplicates.
- std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
- knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
- isApproxEqual),
- knownFrameRates.end());
- return knownFrameRates;
-}
-
-// The Filter is a `bool(const DisplayMode&)` predicate.
-template <typename Filter>
-std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
- std::vector<DisplayModeIterator> sortedModes;
- sortedModes.reserve(modes.size());
-
- for (auto it = modes.begin(); it != modes.end(); ++it) {
- const auto& [id, mode] = *it;
-
- if (filter(*mode)) {
- ALOGV("%s: including mode %d", __func__, id.value());
- sortedModes.push_back(it);
- }
- }
-
- std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
- const auto& mode1 = it1->second;
- const auto& mode2 = it2->second;
-
- if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
- return mode1->getGroup() > mode2->getGroup();
- }
-
- return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
- });
-
- return sortedModes;
-}
-
-bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
- for (const auto it1 : sortedModes) {
- const auto& mode1 = it1->second;
- for (const auto it2 : sortedModes) {
- const auto& mode2 = it2->second;
-
- if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
- return true;
- }
- }
- }
- return false;
-}
-
-} // namespace
-
-struct RefreshRateConfigs::RefreshRateScoreComparator {
- bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
- const auto& [modeIt, overallScore, _] = lhs;
-
- std::string name = to_string(modeIt->second->getFps());
- ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
-
- ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
-
- constexpr float kEpsilon = 0.0001f;
- if (std::abs(overallScore - rhs.overallScore) > kEpsilon) {
- return overallScore > rhs.overallScore;
- }
-
- // If overallScore tie we will pick the higher refresh rate if
- // high refresh rate is the priority else the lower refresh rate.
- if (refreshRateOrder == RefreshRateOrder::Descending) {
- using fps_approx_ops::operator>;
- return modeIt->second->getFps() > rhs.modeIt->second->getFps();
- } else {
- using fps_approx_ops::operator<;
- return modeIt->second->getFps() < rhs.modeIt->second->getFps();
- }
- }
-
- const RefreshRateOrder refreshRateOrder;
-};
-
-std::string RefreshRateConfigs::Policy::toString() const {
- return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
- ", primaryRange=%s, appRequestRange=%s}",
- defaultMode.value(), allowGroupSwitching ? "true" : "false",
- to_string(primaryRange).c_str(), to_string(appRequestRange).c_str());
-}
-
-std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
- nsecs_t displayPeriod) const {
- auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
- if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
- std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
- quotient++;
- remainder = 0;
- }
-
- return {quotient, remainder};
-}
-
-float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
- Fps refreshRate) const {
- constexpr float kScoreForFractionalPairs = .8f;
-
- const auto displayPeriod = refreshRate.getPeriodNsecs();
- const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
- if (layer.vote == LayerVoteType::ExplicitDefault) {
- // Find the actual rate the layer will render, assuming
- // that layerPeriod is the minimal period to render a frame.
- // For example if layerPeriod is 20ms and displayPeriod is 16ms,
- // then the actualLayerPeriod will be 32ms, because it is the
- // smallest multiple of the display period which is >= layerPeriod.
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
-
- // Because of the threshold we used above it's possible that score is slightly
- // above 1.
- return std::min(1.0f,
- static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
- }
-
- if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
- layer.vote == LayerVoteType::Heuristic) {
- if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
- return kScoreForFractionalPairs;
- }
-
- // Calculate how many display vsyncs we need to present a single frame for this
- // layer
- const auto [displayFramesQuotient, displayFramesRemainder] =
- getDisplayFrames(layerPeriod, displayPeriod);
- static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
- if (displayFramesRemainder == 0) {
- // Layer desired refresh rate matches the display rate.
- return 1.0f;
- }
-
- if (displayFramesQuotient == 0) {
- // Layer desired refresh rate is higher than the display rate.
- return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
- (1.0f / (MAX_FRAMES_TO_FIT + 1));
- }
-
- // Layer desired refresh rate is lower than the display rate. Check how well it fits
- // the cadence.
- auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
- int iter = 2;
- while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
- diff = diff - (displayPeriod - diff);
- iter++;
- }
-
- return (1.0f / iter);
- }
-
- return 0;
-}
-
-float RefreshRateConfigs::calculateRefreshRateScoreForFps(Fps refreshRate) const {
- const float ratio =
- refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
- // Use ratio^2 to get a lower score the more we get further from peak
- return ratio * ratio;
-}
-
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
- bool isSeamlessSwitch) const {
- // Slightly prefer seamless switches.
- constexpr float kSeamedSwitchPenalty = 0.95f;
- const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-
- // If the layer wants Max, give higher score to the higher refresh rate
- if (layer.vote == LayerVoteType::Max) {
- return calculateRefreshRateScoreForFps(refreshRate);
- }
-
- if (layer.vote == LayerVoteType::ExplicitExact) {
- const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
- if (mSupportsFrameRateOverrideByContent) {
- // Since we support frame rate override, allow refresh rates which are
- // multiples of the layer's request, as those apps would be throttled
- // down to run at the desired refresh rate.
- return divisor > 0;
- }
-
- return divisor == 1;
- }
-
- // If the layer frame rate is a divisor of the refresh rate it should score
- // the highest score.
- if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
- return 1.0f * seamlessness;
- }
-
- // The layer frame rate is not a divisor of the refresh rate,
- // there is a small penalty attached to the score to favor the frame rates
- // the exactly matches the display refresh rate or a multiple.
- constexpr float kNonExactMatchingPenalty = 0.95f;
- return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
- kNonExactMatchingPenalty;
-}
-
-auto RefreshRateConfigs::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
- -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
- std::lock_guard lock(mLock);
-
- if (mGetRankedRefreshRatesCache &&
- mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) {
- return mGetRankedRefreshRatesCache->result;
- }
-
- const auto result = getRankedRefreshRatesLocked(layers, signals);
- mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result};
- return result;
-}
-
-auto RefreshRateConfigs::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
- -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
- using namespace fps_approx_ops;
- ATRACE_CALL();
- ALOGV("%s: %zu layers", __func__, layers.size());
-
- const auto& activeMode = *getActiveModeItLocked()->second;
-
- // Keep the display at max refresh rate for the duration of powering on the display.
- if (signals.powerOnImminent) {
- ALOGV("Power On Imminent");
- return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending),
- GlobalSignals{.powerOnImminent = true}};
- }
-
- int noVoteLayers = 0;
- int minVoteLayers = 0;
- int maxVoteLayers = 0;
- int explicitDefaultVoteLayers = 0;
- int explicitExactOrMultipleVoteLayers = 0;
- int explicitExact = 0;
- int seamedFocusedLayers = 0;
-
- for (const auto& layer : layers) {
- switch (layer.vote) {
- case LayerVoteType::NoVote:
- noVoteLayers++;
- break;
- case LayerVoteType::Min:
- minVoteLayers++;
- break;
- case LayerVoteType::Max:
- maxVoteLayers++;
- break;
- case LayerVoteType::ExplicitDefault:
- explicitDefaultVoteLayers++;
- break;
- case LayerVoteType::ExplicitExactOrMultiple:
- explicitExactOrMultipleVoteLayers++;
- break;
- case LayerVoteType::ExplicitExact:
- explicitExact++;
- break;
- case LayerVoteType::Heuristic:
- break;
- }
-
- if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
- seamedFocusedLayers++;
- }
- }
-
- const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
- explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
-
- const Policy* policy = getCurrentPolicyLocked();
- const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
-
- // If the default mode group is different from the group of current mode,
- // this means a layer requesting a seamed mode switch just disappeared and
- // we should switch back to the default group.
- // However if a seamed layer is still present we anchor around the group
- // of the current mode, in order to prevent unnecessary seamed mode switches
- // (e.g. when pausing a video playback).
- const auto anchorGroup =
- seamedFocusedLayers > 0 ? activeMode.getGroup() : defaultMode->getGroup();
-
- // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
- // selected a refresh rate to see if we should apply touch boost.
- if (signals.touch && !hasExplicitVoteLayers) {
- ALOGV("Touch Boost");
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
- GlobalSignals{.touch = true}};
- }
-
- // If the primary range consists of a single refresh rate then we can only
- // move out the of range if layers explicitly request a different refresh
- // rate.
- const bool primaryRangeIsSingleRate =
- isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
-
- if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
- ALOGV("Idle");
- return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
- GlobalSignals{.idle = true}};
- }
-
- if (layers.empty() || noVoteLayers == layers.size()) {
- ALOGV("No layers with votes");
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
- kNoSignals};
- }
-
- // Only if all layers want Min we should return Min
- if (noVoteLayers + minVoteLayers == layers.size()) {
- ALOGV("All layers Min");
- return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
- kNoSignals};
- }
-
- // Find the best refresh rate based on score
- std::vector<RefreshRateScore> scores;
- scores.reserve(mAppRequestRefreshRates.size());
-
- for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
- scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
- }
-
- for (const auto& layer : layers) {
- ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
- ftl::enum_string(layer.vote).c_str(), layer.weight,
- layer.desiredRefreshRate.getValue());
- if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
- continue;
- }
-
- const auto weight = layer.weight;
-
- for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
- const auto& [id, mode] = *modeIt;
- const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup();
-
- if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
- ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
- to_string(activeMode).c_str());
- continue;
- }
-
- if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
- !layer.focused) {
- ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
- " Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
- to_string(activeMode).c_str());
- continue;
- }
-
- // Layers with default seamlessness vote for the current mode group if
- // there are layers with seamlessness=SeamedAndSeamless and for the default
- // mode group otherwise. In second case, if the current mode group is different
- // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
- // disappeared.
- const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
- if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
- ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
- to_string(*mode).c_str(), to_string(activeMode).c_str());
- continue;
- }
-
- const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps());
- if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
- !(layer.focused &&
- (layer.vote == LayerVoteType::ExplicitDefault ||
- layer.vote == LayerVoteType::ExplicitExact))) {
- // Only focused layers with ExplicitDefault frame rate settings are allowed to score
- // refresh rates outside the primary range.
- continue;
- }
-
- const float layerScore =
- calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
- const float weightedLayerScore = weight * layerScore;
-
- // Layer with fixed source has a special consideration which depends on the
- // mConfig.frameRateMultipleThreshold. We don't want these layers to score
- // refresh rates above the threshold, but we also don't want to favor the lower
- // ones by having a greater number of layers scoring them. Instead, we calculate
- // the score independently for these layers and later decide which
- // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
- // score 120 Hz, but desired 60 fps should contribute to the score.
- const bool fixedSourceLayer = [](LayerVoteType vote) {
- switch (vote) {
- case LayerVoteType::ExplicitExactOrMultiple:
- case LayerVoteType::Heuristic:
- return true;
- case LayerVoteType::NoVote:
- case LayerVoteType::Min:
- case LayerVoteType::Max:
- case LayerVoteType::ExplicitDefault:
- case LayerVoteType::ExplicitExact:
- return false;
- }
- }(layer.vote);
- const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
- layer.desiredRefreshRate <
- Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
- if (fixedSourceLayer && layerBelowThreshold) {
- const bool modeAboveThreshold =
- mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
- if (modeAboveThreshold) {
- ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
- formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
- layerScore);
- fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
- } else {
- ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
- formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
- layerScore);
- fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
- }
- } else {
- ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
- to_string(mode->getFps()).c_str(), layerScore);
- overallScore += weightedLayerScore;
- }
- }
- }
-
- // We want to find the best refresh rate without the fixed source layers,
- // so we could know whether we should add the modeAboveThreshold scores or not.
- // If the best refresh rate is already above the threshold, it means that
- // some non-fixed source layers already scored it, so we can just add the score
- // for all fixed source layers, even the ones that are above the threshold.
- const bool maxScoreAboveThreshold = [&] {
- if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
- return false;
- }
-
- const auto maxScoreIt =
- std::max_element(scores.begin(), scores.end(),
- [](RefreshRateScore max, RefreshRateScore current) {
- const auto& [modeIt, overallScore, _] = current;
- return overallScore > max.overallScore;
- });
- ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
- "refresh rate multiples",
- to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
- maxScoreAboveThreshold ? "above" : "below");
- return maxScoreIt->modeIt->second->getFps() >=
- Fps::fromValue(mConfig.frameRateMultipleThreshold);
- }();
-
- // Now we can add the fixed rate layers score
- for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
- overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
- if (maxScoreAboveThreshold) {
- overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
- }
- ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
- overallScore);
- }
-
- // Now that we scored all the refresh rates we need to pick the one that got the highest
- // overallScore. Sort the scores based on their overallScore in descending order of priority.
- const RefreshRateOrder refreshRateOrder =
- maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending;
- std::sort(scores.begin(), scores.end(),
- RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
- std::vector<RefreshRateRanking> rankedRefreshRates;
- rankedRefreshRates.reserve(scores.size());
-
- std::transform(scores.begin(), scores.end(), back_inserter(rankedRefreshRates),
- [](const RefreshRateScore& score) {
- return RefreshRateRanking{score.modeIt->second, score.overallScore};
- });
-
- if (primaryRangeIsSingleRate) {
- // If we never scored any layers, then choose the rate from the primary
- // range instead of picking a random score from the app range.
- if (std::all_of(scores.begin(), scores.end(),
- [](RefreshRateScore score) { return score.overallScore == 0; })) {
- ALOGV("Layers not scored");
- return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
- kNoSignals};
- } else {
- return {rankedRefreshRates, kNoSignals};
- }
- }
-
- // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
- // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
- // vote we should not change it if we get a touch event. Only apply touch boost if it will
- // actually increase the refresh rate over the normal selection.
- const bool touchBoostForExplicitExact = [&] {
- if (mSupportsFrameRateOverrideByContent) {
- // Enable touch boost if there are other layers besides exact
- return explicitExact + noVoteLayers != layers.size();
- } else {
- // Enable touch boost if there are no exact layers
- return explicitExact == 0;
- }
- }();
-
- const auto& touchRefreshRates =
- getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending);
- using fps_approx_ops::operator<;
-
- if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- scores.front().modeIt->second->getFps() <
- touchRefreshRates.front().displayModePtr->getFps()) {
- ALOGV("Touch Boost");
- return {touchRefreshRates, GlobalSignals{.touch = true}};
- }
-
- return {rankedRefreshRates, kNoSignals};
-}
-
-std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
-groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
- std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
- for (const auto& layer : layers) {
- auto iter = layersByUid.emplace(layer.ownerUid,
- std::vector<const RefreshRateConfigs::LayerRequirement*>());
- auto& layersWithSameUid = iter.first->second;
- layersWithSameUid.push_back(&layer);
- }
-
- // Remove uids that can't have a frame rate override
- for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
- const auto& layersWithSameUid = iter->second;
- bool skipUid = false;
- for (const auto& layer : layersWithSameUid) {
- if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
- layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
- skipUid = true;
- break;
- }
- }
- if (skipUid) {
- iter = layersByUid.erase(iter);
- } else {
- ++iter;
- }
- }
-
- return layersByUid;
-}
-
-RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
- const std::vector<LayerRequirement>& layers, Fps displayRefreshRate,
- GlobalSignals globalSignals) const {
- ATRACE_CALL();
-
- ALOGV("%s: %zu layers", __func__, layers.size());
-
- std::lock_guard lock(mLock);
-
- std::vector<RefreshRateScore> scores;
- scores.reserve(mDisplayModes.size());
-
- for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
- scores.emplace_back(RefreshRateScore{it, 0.0f});
- }
-
- std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) {
- const auto& mode1 = lhs.modeIt->second;
- const auto& mode2 = rhs.modeIt->second;
- return isStrictlyLess(mode1->getFps(), mode2->getFps());
- });
-
- std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
- groupLayersByUid(layers);
- UidToFrameRateOverride frameRateOverrides;
- for (const auto& [uid, layersWithSameUid] : layersByUid) {
- // Layers with ExplicitExactOrMultiple expect touch boost
- const bool hasExplicitExactOrMultiple =
- std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(),
- [](const auto& layer) {
- return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
- });
-
- if (globalSignals.touch && hasExplicitExactOrMultiple) {
- continue;
- }
-
- for (auto& [_, score, _1] : scores) {
- score = 0;
- }
-
- for (const auto& layer : layersWithSameUid) {
- if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
- continue;
- }
-
- LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
- layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
- layer->vote != LayerVoteType::ExplicitExact);
- for (auto& [modeIt, score, _] : scores) {
- constexpr bool isSeamlessSwitch = true;
- const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
- isSeamlessSwitch);
- score += layer->weight * layerScore;
- }
- }
-
- // We just care about the refresh rates which are a divisor of the
- // display refresh rate
- const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) {
- const auto& [id, mode] = *score.modeIt;
- return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0;
- });
- scores.erase(it, scores.end());
-
- // If we never scored any layers, we don't have a preferred frame rate
- if (std::all_of(scores.begin(), scores.end(),
- [](RefreshRateScore score) { return score.overallScore == 0; })) {
- continue;
- }
-
- // Now that we scored all the refresh rates we need to pick the lowest refresh rate
- // that got the highest score.
- const DisplayModePtr& bestRefreshRate =
- std::min_element(scores.begin(), scores.end(),
- RefreshRateScoreComparator{.refreshRateOrder =
- RefreshRateOrder::Ascending})
- ->modeIt->second;
- frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
- }
-
- return frameRateOverrides;
-}
-
-std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
- std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
- std::lock_guard lock(mLock);
-
- const DisplayModePtr& current = desiredActiveModeId
- ? mDisplayModes.get(*desiredActiveModeId)->get()
- : getActiveModeItLocked()->second;
-
- const DisplayModePtr& min = mMinRefreshRateModeIt->second;
- if (current == min) {
- return {};
- }
-
- const auto& mode = timerExpired ? min : current;
- return mode->getFps();
-}
-
-const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
- const auto& activeMode = *getActiveModeItLocked()->second;
-
- for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
- const auto& mode = modeIt->second;
- if (activeMode.getGroup() == mode->getGroup()) {
- return mode;
- }
- }
-
- ALOGE("Can't find min refresh rate by policy with the same mode group as the current mode %s",
- to_string(activeMode).c_str());
-
- // Default to the lowest refresh rate.
- return mPrimaryRefreshRates.front()->second;
-}
-
-const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
- for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
- const auto& mode = (*it)->second;
- if (anchorGroup == mode->getGroup()) {
- return mode;
- }
- }
-
- ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
-
- // Default to the highest refresh rate.
- return mPrimaryRefreshRates.back()->second;
-}
-
-std::vector<RefreshRateRanking> RefreshRateConfigs::getRefreshRatesByPolicyLocked(
- std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
- std::vector<RefreshRateRanking> rankings;
- const auto makeRanking = [&](const DisplayModeIterator it) REQUIRES(mLock) {
- const auto& mode = it->second;
- const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
- const float score = calculateRefreshRateScoreForFps(mode->getFps());
- if (!anchorGroupOpt || mode->getGroup() == anchorGroupOpt) {
- rankings.push_back(RefreshRateRanking{mode, inverseScore ? 1.0f / score : score});
- }
- };
-
- if (refreshRateOrder == RefreshRateOrder::Ascending) {
- std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), makeRanking);
- } else {
- std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), makeRanking);
- }
-
- if (!rankings.empty() || !anchorGroupOpt) {
- return rankings;
- }
-
- ALOGW("Can't find %s refresh rate by policy with the same mode group"
- " as the mode group %d",
- refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
-
- return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder);
-}
-
-DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {
- std::lock_guard lock(mLock);
- return getActiveModeItLocked()->second;
-}
-
-const DisplayMode& RefreshRateConfigs::getActiveMode() const {
- // Reads from kMainThreadContext do not require mLock.
- ftl::FakeGuard guard(mLock);
- return *mActiveModeIt->second;
-}
-
-DisplayModeIterator RefreshRateConfigs::getActiveModeItLocked() const {
- // Reads under mLock do not require kMainThreadContext.
- return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt);
-}
-
-void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
- std::lock_guard lock(mLock);
-
- // Invalidate the cached invocation to getRankedRefreshRates. This forces
- // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
- mGetRankedRefreshRatesCache.reset();
-
- mActiveModeIt = mDisplayModes.find(modeId);
- LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
-}
-
-RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId,
- Config config)
- : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
- initializeIdleTimer();
- FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
-}
-
-void RefreshRateConfigs::initializeIdleTimer() {
- if (mConfig.idleTimerTimeout > 0ms) {
- mIdleTimer.emplace(
- "IdleTimer", mConfig.idleTimerTimeout,
- [this] {
- std::scoped_lock lock(mIdleTimerCallbacksMutex);
- if (const auto callbacks = getIdleTimerCallbacks()) {
- callbacks->onReset();
- }
- },
- [this] {
- std::scoped_lock lock(mIdleTimerCallbacksMutex);
- if (const auto callbacks = getIdleTimerCallbacks()) {
- callbacks->onExpired();
- }
- });
- }
-}
-
-void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
- std::lock_guard lock(mLock);
-
- // Invalidate the cached invocation to getRankedRefreshRates. This forces
- // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
- mGetRankedRefreshRatesCache.reset();
-
- mDisplayModes = std::move(modes);
- mActiveModeIt = mDisplayModes.find(activeModeId);
- LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
-
- const auto sortedModes =
- sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
- mMinRefreshRateModeIt = sortedModes.front();
- mMaxRefreshRateModeIt = sortedModes.back();
-
- // Reset the policy because the old one may no longer be valid.
- mDisplayManagerPolicy = {};
- mDisplayManagerPolicy.defaultMode = activeModeId;
-
- mSupportsFrameRateOverrideByContent =
- mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes);
-
- constructAvailableRefreshRates();
-}
-
-bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
- // defaultMode must be a valid mode, and within the given refresh rate range.
- if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
- if (!policy.primaryRange.includes(mode->get()->getFps())) {
- ALOGE("Default mode is not in the primary range.");
- return false;
- }
- } else {
- ALOGE("Default mode is not found.");
- return false;
- }
-
- using namespace fps_approx_ops;
- return policy.appRequestRange.min <= policy.primaryRange.min &&
- policy.appRequestRange.max >= policy.primaryRange.max;
-}
-
-status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
- std::lock_guard lock(mLock);
- if (!isPolicyValidLocked(policy)) {
- ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
- return BAD_VALUE;
- }
- mGetRankedRefreshRatesCache.reset();
- Policy previousPolicy = *getCurrentPolicyLocked();
- mDisplayManagerPolicy = policy;
- if (*getCurrentPolicyLocked() == previousPolicy) {
- return CURRENT_POLICY_UNCHANGED;
- }
- constructAvailableRefreshRates();
- return NO_ERROR;
-}
-
-status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
- std::lock_guard lock(mLock);
- if (policy && !isPolicyValidLocked(*policy)) {
- return BAD_VALUE;
- }
- mGetRankedRefreshRatesCache.reset();
- Policy previousPolicy = *getCurrentPolicyLocked();
- mOverridePolicy = policy;
- if (*getCurrentPolicyLocked() == previousPolicy) {
- return CURRENT_POLICY_UNCHANGED;
- }
- constructAvailableRefreshRates();
- return NO_ERROR;
-}
-
-const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
- return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
-}
-
-RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const {
- std::lock_guard lock(mLock);
- return *getCurrentPolicyLocked();
-}
-
-RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const {
- std::lock_guard lock(mLock);
- return mDisplayManagerPolicy;
-}
-
-bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
- std::lock_guard lock(mLock);
- return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
- [modeId](DisplayModeIterator modeIt) {
- return modeIt->second->getId() == modeId;
- });
-}
-
-void RefreshRateConfigs::constructAvailableRefreshRates() {
- // Filter modes based on current policy and sort on refresh rate.
- const Policy* policy = getCurrentPolicyLocked();
- ALOGV("%s: %s ", __func__, policy->toString().c_str());
-
- const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
-
- const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
- const auto filter = [&](const DisplayMode& mode) {
- return mode.getResolution() == defaultMode->getResolution() &&
- mode.getDpi() == defaultMode->getDpi() &&
- (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
- range.includes(mode.getFps());
- };
-
- const auto modes = sortByRefreshRate(mDisplayModes, filter);
- LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
- to_string(range).c_str());
-
- const auto stringifyModes = [&] {
- std::string str;
- for (const auto modeIt : modes) {
- str += to_string(modeIt->second->getFps());
- str.push_back(' ');
- }
- return str;
- };
- ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
-
- return modes;
- };
-
- mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary");
- mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request");
-}
-
-Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
- using namespace fps_approx_ops;
-
- if (frameRate <= mKnownFrameRates.front()) {
- return mKnownFrameRates.front();
- }
-
- if (frameRate >= mKnownFrameRates.back()) {
- return mKnownFrameRates.back();
- }
-
- auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
- isStrictlyLess);
-
- const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
- const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
- return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
-}
-
-RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
- std::lock_guard lock(mLock);
-
- const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
- const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked();
-
- // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
- // the min allowed refresh rate is higher than the device min, we do not want to enable the
- // timer.
- if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
- return KernelIdleTimerAction::TurnOff;
- }
-
- const DisplayModePtr& maxByPolicy =
- getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
- if (minByPolicy == maxByPolicy) {
- // Turn on the timer when the min of the primary range is below the device min.
- if (const Policy* currentPolicy = getCurrentPolicyLocked();
- isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) {
- return KernelIdleTimerAction::TurnOn;
- }
- return KernelIdleTimerAction::TurnOff;
- }
-
- // Turn on the timer in all other cases.
- return KernelIdleTimerAction::TurnOn;
-}
-
-int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
- // This calculation needs to be in sync with the java code
- // in DisplayManagerService.getDisplayInfoForFrameRateOverride
-
- // The threshold must be smaller than 0.001 in order to differentiate
- // between the fractional pairs (e.g. 59.94 and 60).
- constexpr float kThreshold = 0.0009f;
- const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue();
- const auto numPeriodsRounded = std::round(numPeriods);
- if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
- return 0;
- }
-
- return static_cast<int>(numPeriodsRounded);
-}
-
-bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
- if (isStrictlyLess(bigger, smaller)) {
- return isFractionalPairOrMultiple(bigger, smaller);
- }
-
- const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
- constexpr float kCoef = 1000.f / 1001.f;
- return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
- isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
-}
-
-void RefreshRateConfigs::dump(std::string& result) const {
- using namespace std::string_literals;
-
- std::lock_guard lock(mLock);
-
- const auto activeModeId = getActiveModeItLocked()->first;
- result += " activeModeId="s;
- result += std::to_string(activeModeId.value());
-
- result += "\n displayModes=\n"s;
- for (const auto& [id, mode] : mDisplayModes) {
- result += " "s;
- result += to_string(*mode);
- result += '\n';
- }
-
- base::StringAppendF(&result, " displayManagerPolicy=%s\n",
- mDisplayManagerPolicy.toString().c_str());
-
- if (const Policy& currentPolicy = *getCurrentPolicyLocked();
- mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
- base::StringAppendF(&result, " overridePolicy=%s\n", currentPolicy.toString().c_str());
- }
-
- base::StringAppendF(&result, " supportsFrameRateOverrideByContent=%s\n",
- mSupportsFrameRateOverrideByContent ? "true" : "false");
-
- result += " idleTimer="s;
- if (mIdleTimer) {
- result += mIdleTimer->dump();
- } else {
- result += "off"s;
- }
-
- if (const auto controller = mConfig.kernelIdleTimerController) {
- base::StringAppendF(&result, " (kernel via %s)", ftl::enum_string(*controller).c_str());
- } else {
- result += " (platform)"s;
- }
-
- result += '\n';
-}
-
-std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() {
- return mConfig.idleTimerTimeout;
-}
-
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
new file mode 100644
index 0000000..a05d3df
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -0,0 +1,1304 @@
+/*
+ * Copyright 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <chrono>
+#include <cmath>
+#include <deque>
+#include <map>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
+#include <ftl/unit.h>
+#include <scheduler/FrameRateMode.h>
+#include <utils/Trace.h>
+
+#include "../SurfaceFlingerProperties.h"
+#include "RefreshRateSelector.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateSelector"
+
+namespace android::scheduler {
+namespace {
+
+struct RefreshRateScore {
+ FrameRateMode frameRateMode;
+ float overallScore;
+ struct {
+ float modeBelowThreshold;
+ float modeAboveThreshold;
+ } fixedRateBelowThresholdLayersScore;
+};
+
+constexpr RefreshRateSelector::GlobalSignals kNoSignals;
+
+std::string formatLayerInfo(const RefreshRateSelector::LayerRequirement& layer, float weight) {
+ return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(),
+ ftl::enum_string(layer.vote).c_str(), weight,
+ ftl::enum_string(layer.seamlessness).c_str(),
+ to_string(layer.desiredRefreshRate).c_str());
+}
+
+std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
+ std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
+ knownFrameRates.reserve(knownFrameRates.size() + modes.size());
+
+ // Add all supported refresh rates.
+ for (const auto& [id, mode] : modes) {
+ knownFrameRates.push_back(mode->getFps());
+ }
+
+ // Sort and remove duplicates.
+ std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
+ knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+ isApproxEqual),
+ knownFrameRates.end());
+ return knownFrameRates;
+}
+
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) {
+ std::vector<DisplayModeIterator> sortedModes;
+ sortedModes.reserve(modes.size());
+ for (auto it = modes.begin(); it != modes.end(); ++it) {
+ sortedModes.push_back(it);
+ }
+
+ std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
+ const auto& mode1 = it1->second;
+ const auto& mode2 = it2->second;
+
+ if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
+ return mode1->getGroup() > mode2->getGroup();
+ }
+
+ return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
+ });
+
+ return sortedModes;
+}
+
+std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range,
+ RefreshRateSelector::Config::FrameRateOverride config) {
+ if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) {
+ return {1, 1};
+ }
+
+ using fps_approx_ops::operator/;
+ // use signed type as `fps / range.max` might be 0
+ const auto start = std::max(1, static_cast<int>(fps / range.max) - 1);
+ const auto end = fps /
+ std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
+ fps_approx_ops::operator<);
+
+ return {start, end};
+}
+
+bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
+ for (const auto it1 : sortedModes) {
+ const auto& mode1 = it1->second;
+ for (const auto it2 : sortedModes) {
+ const auto& mode2 = it2->second;
+
+ if (RefreshRateSelector::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+std::string toString(const RefreshRateSelector::PolicyVariant& policy) {
+ using namespace std::string_literals;
+
+ return ftl::match(
+ policy,
+ [](const RefreshRateSelector::DisplayManagerPolicy& policy) {
+ return "DisplayManagerPolicy"s + policy.toString();
+ },
+ [](const RefreshRateSelector::OverridePolicy& policy) {
+ return "OverridePolicy"s + policy.toString();
+ },
+ [](RefreshRateSelector::NoOverridePolicy) { return "NoOverridePolicy"s; });
+}
+
+} // namespace
+
+auto RefreshRateSelector::createFrameRateModes(
+ std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
+ -> std::vector<FrameRateMode> {
+ struct Key {
+ Fps fps;
+ int32_t group;
+ };
+
+ struct KeyLess {
+ bool operator()(const Key& a, const Key& b) const {
+ using namespace fps_approx_ops;
+ if (a.fps != b.fps) {
+ return a.fps < b.fps;
+ }
+
+ // For the same fps the order doesn't really matter, but we still
+ // want the behaviour of a strictly less operator.
+ // We use the group id as the secondary ordering for that.
+ return a.group < b.group;
+ }
+ };
+
+ std::map<Key, DisplayModeIterator, KeyLess> ratesMap;
+ for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+ const auto& [id, mode] = *it;
+
+ if (!filterModes(*mode)) {
+ continue;
+ }
+ const auto [start, end] =
+ divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride);
+ for (auto divisor = start; divisor <= end; divisor++) {
+ const auto fps = mode->getFps() / divisor;
+ using fps_approx_ops::operator<;
+ if (fps < kMinSupportedFrameRate) {
+ break;
+ }
+
+ if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled &&
+ !renderRange.includes(fps)) {
+ continue;
+ }
+
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+ !isNativeRefreshRate(fps)) {
+ continue;
+ }
+
+ const auto [existingIter, emplaceHappened] =
+ ratesMap.try_emplace(Key{fps, mode->getGroup()}, it);
+ if (emplaceHappened) {
+ ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
+ to_string(mode->getFps()).c_str());
+ } else {
+ // We might need to update the map as we found a lower refresh rate
+ if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+ existingIter->second = it;
+ ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
+ to_string(mode->getFps()).c_str());
+ }
+ }
+ }
+ }
+
+ std::vector<FrameRateMode> frameRateModes;
+ frameRateModes.reserve(ratesMap.size());
+ for (const auto& [key, mode] : ratesMap) {
+ frameRateModes.emplace_back(FrameRateMode{key.fps, ftl::as_non_null(mode->second)});
+ }
+
+ // We always want that the lowest frame rate will be corresponding to the
+ // lowest mode for power saving.
+ const auto lowestRefreshRateIt =
+ std::min_element(frameRateModes.begin(), frameRateModes.end(),
+ [](const FrameRateMode& lhs, const FrameRateMode& rhs) {
+ return isStrictlyLess(lhs.modePtr->getFps(),
+ rhs.modePtr->getFps());
+ });
+ frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt);
+
+ return frameRateModes;
+}
+
+struct RefreshRateSelector::RefreshRateScoreComparator {
+ bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
+ const auto& [frameRateMode, overallScore, _] = lhs;
+
+ std::string name = to_string(frameRateMode);
+
+ ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
+ ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
+
+ if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) {
+ return overallScore > rhs.overallScore;
+ }
+
+ if (refreshRateOrder == RefreshRateOrder::Descending) {
+ using fps_approx_ops::operator>;
+ return frameRateMode.fps > rhs.frameRateMode.fps;
+ } else {
+ using fps_approx_ops::operator<;
+ return frameRateMode.fps < rhs.frameRateMode.fps;
+ }
+ }
+
+ const RefreshRateOrder refreshRateOrder;
+};
+
+std::string RefreshRateSelector::Policy::toString() const {
+ return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
+ ", primaryRanges=%s, appRequestRanges=%s}",
+ defaultMode.value(), allowGroupSwitching ? "true" : "false",
+ to_string(primaryRanges).c_str(),
+ to_string(appRequestRanges).c_str());
+}
+
+std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod,
+ nsecs_t displayPeriod) const {
+ auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
+ if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
+ std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+ quotient++;
+ remainder = 0;
+ }
+
+ return {quotient, remainder};
+}
+
+float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
+ Fps refreshRate) const {
+ constexpr float kScoreForFractionalPairs = .8f;
+
+ const auto displayPeriod = refreshRate.getPeriodNsecs();
+ const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+ if (layer.vote == LayerVoteType::ExplicitDefault) {
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal period to render a frame.
+ // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+ // then the actualLayerPeriod will be 32ms, because it is the
+ // smallest multiple of the display period which is >= layerPeriod.
+ auto actualLayerPeriod = displayPeriod;
+ int multiplier = 1;
+ while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+ multiplier++;
+ actualLayerPeriod = displayPeriod * multiplier;
+ }
+
+ // Because of the threshold we used above it's possible that score is slightly
+ // above 1.
+ return std::min(1.0f,
+ static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+ }
+
+ if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+ layer.vote == LayerVoteType::Heuristic) {
+ const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
+
+ // We only want to score this layer as a fractional pair if the content is not
+ // significantly faster than the display rate, at it would cause a significant frame drop.
+ // It is more appropriate to choose a higher display rate even if
+ // a pull-down will be required.
+ constexpr float kMinMultiplier = 0.25f;
+ if (multiplier >= kMinMultiplier &&
+ isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
+ return kScoreForFractionalPairs;
+ }
+
+ // Calculate how many display vsyncs we need to present a single frame for this
+ // layer
+ const auto [displayFramesQuotient, displayFramesRemainder] =
+ getDisplayFrames(layerPeriod, displayPeriod);
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+ if (displayFramesRemainder == 0) {
+ // Layer desired refresh rate matches the display rate.
+ return 1.0f;
+ }
+
+ if (displayFramesQuotient == 0) {
+ // Layer desired refresh rate is higher than the display rate.
+ return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
+ }
+
+ // Layer desired refresh rate is lower than the display rate. Check how well it fits
+ // the cadence.
+ auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+ int iter = 2;
+ while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ return (1.0f / iter);
+ }
+
+ 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
+ return ratio * ratio;
+}
+
+float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
+ bool isSeamlessSwitch) const {
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+ // If the layer wants Max, give higher score to the higher refresh rate
+ if (layer.vote == LayerVoteType::Max) {
+ return calculateDistanceScoreFromMax(refreshRate);
+ }
+
+ if (layer.vote == LayerVoteType::ExplicitExact) {
+ const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
+ if (supportsAppFrameRateOverrideByContent()) {
+ // Since we support frame rate override, allow refresh rates which are
+ // multiples of the layer's request, as those apps would be throttled
+ // down to run at the desired refresh rate.
+ return divisor > 0;
+ }
+
+ return divisor == 1;
+ }
+
+ // If the layer frame rate is a divisor of the refresh rate it should score
+ // the highest score.
+ if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
+ return 1.0f * seamlessness;
+ }
+
+ // The layer frame rate is not a divisor of the refresh rate,
+ // there is a small penalty attached to the score to favor the frame rates
+ // the exactly matches the display refresh rate or a multiple.
+ constexpr float kNonExactMatchingPenalty = 0.95f;
+ return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+ kNonExactMatchingPenalty;
+}
+
+auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const -> RankedFrameRates {
+ std::lock_guard lock(mLock);
+
+ if (mGetRankedFrameRatesCache &&
+ mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+ return mGetRankedFrameRatesCache->result;
+ }
+
+ const auto result = getRankedFrameRatesLocked(layers, signals);
+ mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
+ return result;
+}
+
+auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const
+ -> RankedFrameRates {
+ using namespace fps_approx_ops;
+ ATRACE_CALL();
+ ALOGV("%s: %zu layers", __func__, layers.size());
+
+ const auto& activeMode = *getActiveModeLocked().modePtr;
+
+ // Keep the display at max frame rate for the duration of powering on the display.
+ if (signals.powerOnImminent) {
+ ALOGV("Power On Imminent");
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending),
+ GlobalSignals{.powerOnImminent = true}};
+ }
+
+ int noVoteLayers = 0;
+ int minVoteLayers = 0;
+ int maxVoteLayers = 0;
+ int explicitDefaultVoteLayers = 0;
+ int explicitExactOrMultipleVoteLayers = 0;
+ int explicitExact = 0;
+ int seamedFocusedLayers = 0;
+
+ for (const auto& layer : layers) {
+ switch (layer.vote) {
+ case LayerVoteType::NoVote:
+ noVoteLayers++;
+ break;
+ case LayerVoteType::Min:
+ minVoteLayers++;
+ break;
+ case LayerVoteType::Max:
+ maxVoteLayers++;
+ break;
+ case LayerVoteType::ExplicitDefault:
+ explicitDefaultVoteLayers++;
+ break;
+ case LayerVoteType::ExplicitExactOrMultiple:
+ explicitExactOrMultipleVoteLayers++;
+ break;
+ case LayerVoteType::ExplicitExact:
+ explicitExact++;
+ break;
+ case LayerVoteType::Heuristic:
+ break;
+ }
+
+ if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+ seamedFocusedLayers++;
+ }
+ }
+
+ const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
+ explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+
+ const Policy* policy = getCurrentPolicyLocked();
+ const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
+
+ // If the default mode group is different from the group of current mode,
+ // this means a layer requesting a seamed mode switch just disappeared and
+ // we should switch back to the default group.
+ // However if a seamed layer is still present we anchor around the group
+ // of the current mode, in order to prevent unnecessary seamed mode switches
+ // (e.g. when pausing a video playback).
+ const auto anchorGroup =
+ seamedFocusedLayers > 0 ? activeMode.getGroup() : defaultMode->getGroup();
+
+ // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
+ // selected a refresh rate to see if we should apply touch boost.
+ if (signals.touch && !hasExplicitVoteLayers) {
+ ALOGV("Touch Boost");
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending),
+ GlobalSignals{.touch = true}};
+ }
+
+ // If the primary range consists of a single refresh rate then we can only
+ // move out the of range if layers explicitly request a different refresh
+ // rate.
+ const bool primaryRangeIsSingleRate =
+ isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max);
+
+ if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+ ALOGV("Idle");
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
+ GlobalSignals{.idle = true}};
+ }
+
+ if (layers.empty() || noVoteLayers == layers.size()) {
+ ALOGV("No layers with votes");
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+ }
+
+ // Only if all layers want Min we should return Min
+ if (noVoteLayers + minVoteLayers == layers.size()) {
+ ALOGV("All layers Min");
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
+ }
+
+ // Find the best refresh rate based on score
+ std::vector<RefreshRateScore> scores;
+ scores.reserve(mAppRequestFrameRates.size());
+
+ for (const FrameRateMode& it : mAppRequestFrameRates) {
+ scores.emplace_back(RefreshRateScore{it, 0.0f});
+ }
+
+ for (const auto& layer : layers) {
+ ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
+ ftl::enum_string(layer.vote).c_str(), layer.weight,
+ layer.desiredRefreshRate.getValue());
+ if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
+ continue;
+ }
+
+ const auto weight = layer.weight;
+
+ for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ const auto& [fps, modePtr] = mode;
+ const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup();
+
+ if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
+ ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+ to_string(activeMode).c_str());
+ continue;
+ }
+
+ if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
+ !layer.focused) {
+ ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
+ " Current mode = %s",
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+ to_string(activeMode).c_str());
+ continue;
+ }
+
+ // Layers with default seamlessness vote for the current mode group if
+ // there are layers with seamlessness=SeamedAndSeamless and for the default
+ // mode group otherwise. In second case, if the current mode group is different
+ // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
+ // disappeared.
+ const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup;
+ if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
+ ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
+ to_string(*modePtr).c_str(), to_string(activeMode).c_str());
+ continue;
+ }
+
+ const bool inPrimaryRange = policy->primaryRanges.physical.includes(modePtr->getFps());
+ if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+ !(layer.focused &&
+ (layer.vote == LayerVoteType::ExplicitDefault ||
+ layer.vote == LayerVoteType::ExplicitExact))) {
+ // Only focused layers with ExplicitDefault frame rate settings are allowed to score
+ // refresh rates outside the primary range.
+ continue;
+ }
+
+ const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch);
+ const float weightedLayerScore = weight * layerScore;
+
+ // Layer with fixed source has a special consideration which depends on the
+ // mConfig.frameRateMultipleThreshold. We don't want these layers to score
+ // refresh rates above the threshold, but we also don't want to favor the lower
+ // ones by having a greater number of layers scoring them. Instead, we calculate
+ // the score independently for these layers and later decide which
+ // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
+ // score 120 Hz, but desired 60 fps should contribute to the score.
+ const bool fixedSourceLayer = [](LayerVoteType vote) {
+ switch (vote) {
+ case LayerVoteType::ExplicitExactOrMultiple:
+ case LayerVoteType::Heuristic:
+ return true;
+ case LayerVoteType::NoVote:
+ case LayerVoteType::Min:
+ case LayerVoteType::Max:
+ case LayerVoteType::ExplicitDefault:
+ case LayerVoteType::ExplicitExact:
+ return false;
+ }
+ }(layer.vote);
+ const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
+ layer.desiredRefreshRate <
+ Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
+ if (fixedSourceLayer && layerBelowThreshold) {
+ const bool modeAboveThreshold =
+ modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+ if (modeAboveThreshold) {
+ ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+ to_string(modePtr->getFps()).c_str(), layerScore);
+ fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
+ } else {
+ ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+ to_string(modePtr->getFps()).c_str(), layerScore);
+ fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
+ }
+ } else {
+ ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(),
+ to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore);
+ overallScore += weightedLayerScore;
+ }
+ }
+ }
+
+ // We want to find the best refresh rate without the fixed source layers,
+ // so we could know whether we should add the modeAboveThreshold scores or not.
+ // If the best refresh rate is already above the threshold, it means that
+ // some non-fixed source layers already scored it, so we can just add the score
+ // for all fixed source layers, even the ones that are above the threshold.
+ const bool maxScoreAboveThreshold = [&] {
+ if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
+ return false;
+ }
+
+ const auto maxScoreIt =
+ std::max_element(scores.begin(), scores.end(),
+ [](RefreshRateScore max, RefreshRateScore current) {
+ return current.overallScore > max.overallScore;
+ });
+ ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the "
+ "threshold for "
+ "refresh rate multiples",
+ to_string(maxScoreIt->frameRateMode.fps).c_str(),
+ to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(),
+ maxScoreAboveThreshold ? "above" : "below");
+ return maxScoreIt->frameRateMode.modePtr->getFps() >=
+ Fps::fromValue(mConfig.frameRateMultipleThreshold);
+ }();
+
+ // Now we can add the fixed rate layers score
+ for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
+ if (maxScoreAboveThreshold) {
+ overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
+ }
+ ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
+ to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore);
+ }
+
+ // Now that we scored all the refresh rates we need to pick the one that got the highest
+ // overallScore. Sort the scores based on their overallScore in descending order of priority.
+ const RefreshRateOrder refreshRateOrder =
+ maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending;
+ std::sort(scores.begin(), scores.end(),
+ RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
+
+ FrameRateRanking ranking;
+ ranking.reserve(scores.size());
+
+ std::transform(scores.begin(), scores.end(), back_inserter(ranking),
+ [](const RefreshRateScore& score) {
+ return ScoredFrameRate{score.frameRateMode, score.overallScore};
+ });
+
+ const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
+ return score.overallScore == 0;
+ });
+
+ if (primaryRangeIsSingleRate) {
+ // If we never scored any layers, then choose the rate from the primary
+ // range instead of picking a random score from the app range.
+ if (noLayerScore) {
+ ALOGV("Layers not scored");
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+ } else {
+ return {ranking, kNoSignals};
+ }
+ }
+
+ // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
+ // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
+ // vote we should not change it if we get a touch event. Only apply touch boost if it will
+ // actually increase the refresh rate over the normal selection.
+ const bool touchBoostForExplicitExact = [&] {
+ if (supportsAppFrameRateOverrideByContent()) {
+ // Enable touch boost if there are other layers besides exact
+ return explicitExact + noVoteLayers != layers.size();
+ } else {
+ // Enable touch boost if there are no exact layers
+ return explicitExact == 0;
+ }
+ }();
+
+ const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
+ using fps_approx_ops::operator<;
+
+ if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+ scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
+ ALOGV("Touch Boost");
+ return {touchRefreshRates, GlobalSignals{.touch = true}};
+ }
+
+ // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
+ // current config
+ if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+ const auto preferredDisplayMode = activeMode.getId();
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
+ kNoSignals};
+ }
+
+ return {ranking, kNoSignals};
+}
+
+using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
+using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
+
+PerUidLayerRequirements groupLayersByUid(
+ const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
+ PerUidLayerRequirements layersByUid;
+ for (const auto& layer : layers) {
+ const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
+ auto& layersWithSameUid = it->second;
+ layersWithSameUid.push_back(&layer);
+ }
+
+ // Remove uids that can't have a frame rate override
+ for (auto it = layersByUid.begin(); it != layersByUid.end();) {
+ const auto& layersWithSameUid = it->second;
+ bool skipUid = false;
+ for (const auto& layer : layersWithSameUid) {
+ using LayerVoteType = RefreshRateSelector::LayerVoteType;
+
+ if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
+ skipUid = true;
+ break;
+ }
+ }
+ if (skipUid) {
+ it = layersByUid.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ return layersByUid;
+}
+
+auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
+ Fps displayRefreshRate,
+ GlobalSignals globalSignals) const
+ -> UidToFrameRateOverride {
+ ATRACE_CALL();
+ if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) {
+ return {};
+ }
+
+ ALOGV("%s: %zu layers", __func__, layers.size());
+ std::lock_guard lock(mLock);
+
+ const auto* policyPtr = getCurrentPolicyLocked();
+ // We don't want to run lower than 30fps
+ const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);
+
+ using fps_approx_ops::operator/;
+ const unsigned numMultiples = displayRefreshRate / minFrameRate;
+
+ std::vector<std::pair<Fps, float>> scoredFrameRates;
+ scoredFrameRates.reserve(numMultiples);
+
+ for (unsigned n = numMultiples; n > 0; n--) {
+ const Fps divisor = displayRefreshRate / n;
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+ !isNativeRefreshRate(divisor)) {
+ continue;
+ }
+
+ if (policyPtr->appRequestRanges.render.includes(divisor)) {
+ ALOGV("%s: adding %s as a potential frame rate", __func__, to_string(divisor).c_str());
+ scoredFrameRates.emplace_back(divisor, 0);
+ }
+ }
+
+ const auto layersByUid = groupLayersByUid(layers);
+ UidToFrameRateOverride frameRateOverrides;
+ for (const auto& [uid, layersWithSameUid] : layersByUid) {
+ // Layers with ExplicitExactOrMultiple expect touch boost
+ const bool hasExplicitExactOrMultiple =
+ std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(),
+ [](const auto& layer) {
+ return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
+ });
+
+ if (globalSignals.touch && hasExplicitExactOrMultiple) {
+ continue;
+ }
+
+ for (auto& [_, score] : scoredFrameRates) {
+ score = 0;
+ }
+
+ for (const auto& layer : layersWithSameUid) {
+ if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+ continue;
+ }
+
+ LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
+ layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
+ layer->vote != LayerVoteType::ExplicitExact);
+ for (auto& [fps, score] : scoredFrameRates) {
+ constexpr bool isSeamlessSwitch = true;
+ const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
+ score += layer->weight * layerScore;
+ }
+ }
+
+ // If we never scored any layers, we don't have a preferred frame rate
+ if (std::all_of(scoredFrameRates.begin(), scoredFrameRates.end(),
+ [](const auto& scoredFrameRate) {
+ const auto [_, score] = scoredFrameRate;
+ return score == 0;
+ })) {
+ continue;
+ }
+
+ // Now that we scored all the refresh rates we need to pick the lowest refresh rate
+ // that got the highest score.
+ const auto [overrideFps, _] =
+ *std::max_element(scoredFrameRates.begin(), scoredFrameRates.end(),
+ [](const auto& lhsPair, const auto& rhsPair) {
+ const float lhs = lhsPair.second;
+ const float rhs = rhsPair.second;
+ return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
+ });
+ ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
+ frameRateOverrides.emplace(uid, overrideFps);
+ }
+
+ return frameRateOverrides;
+}
+
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
+ std::optional<DisplayModeId> desiredActiveModeId, 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->getFps(), ftl::as_non_null(modePtr)};
+ }
+
+ return getActiveModeLocked();
+ }();
+
+ const DisplayModePtr& min = mMinRefreshRateModeIt->second;
+ if (current.modePtr->getId() == min->getId()) {
+ return {};
+ }
+
+ return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
+}
+
+const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
+ const auto& activeMode = *getActiveModeLocked().modePtr;
+
+ for (const FrameRateMode& mode : mPrimaryFrameRates) {
+ if (activeMode.getGroup() == mode.modePtr->getGroup()) {
+ return mode.modePtr.get();
+ }
+ }
+
+ ALOGE("Can't find min refresh rate by policy with the same mode group as the current mode %s",
+ to_string(activeMode).c_str());
+
+ // Default to the lowest refresh rate.
+ return mPrimaryFrameRates.front().modePtr.get();
+}
+
+const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
+ const ftl::NonNull<DisplayModePtr>* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
+ const ftl::NonNull<DisplayModePtr>* max = &mPrimaryFrameRates.back().modePtr;
+
+ bool maxByAnchorFound = false;
+ for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
+ using namespace fps_approx_ops;
+ if (it->modePtr->getFps() > (*max)->getFps()) {
+ max = &it->modePtr;
+ }
+
+ if (anchorGroup == it->modePtr->getGroup() &&
+ it->modePtr->getFps() >= (*maxByAnchor)->getFps()) {
+ maxByAnchorFound = true;
+ maxByAnchor = &it->modePtr;
+ }
+ }
+
+ if (maxByAnchorFound) {
+ return maxByAnchor->get();
+ }
+
+ ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
+
+ // Default to the highest refresh rate.
+ return max->get();
+}
+
+auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt) const
+ -> FrameRateRanking {
+ const char* const whence = __func__;
+ std::deque<ScoredFrameRate> ranking;
+ const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+ const auto& modePtr = frameRateMode.modePtr;
+ if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
+ return;
+ }
+
+ float score = calculateDistanceScoreFromMax(frameRateMode.fps);
+ const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
+ if (inverseScore) {
+ score = 1.0f / score;
+ }
+ if (preferredDisplayModeOpt) {
+ if (*preferredDisplayModeOpt == modePtr->getId()) {
+ constexpr float kScore = std::numeric_limits<float>::max();
+ ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
+ return;
+ }
+ constexpr float kNonPreferredModePenalty = 0.95f;
+ score *= kNonPreferredModePenalty;
+ }
+ ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
+ to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
+ ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
+ };
+
+ if (refreshRateOrder == RefreshRateOrder::Ascending) {
+ std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate);
+ } else {
+ std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate);
+ }
+
+ if (!ranking.empty() || !anchorGroupOpt) {
+ return {ranking.begin(), ranking.end()};
+ }
+
+ ALOGW("Can't find %s refresh rate by policy with the same mode group"
+ " as the mode group %d",
+ refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
+
+ constexpr std::optional<int> kNoAnchorGroup = std::nullopt;
+ return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
+}
+
+FrameRateMode RefreshRateSelector::getActiveMode() const {
+ std::lock_guard lock(mLock);
+ return getActiveModeLocked();
+}
+
+const FrameRateMode& RefreshRateSelector::getActiveModeLocked() const {
+ return *mActiveModeOpt;
+}
+
+void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
+ std::lock_guard lock(mLock);
+
+ // Invalidate the cached invocation to getRankedFrameRates. This forces
+ // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+ mGetRankedFrameRatesCache.reset();
+
+ const auto activeModeOpt = mDisplayModes.get(modeId);
+ LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+
+ mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())});
+}
+
+RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId,
+ Config config)
+ : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
+ initializeIdleTimer();
+ FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
+}
+
+void RefreshRateSelector::initializeIdleTimer() {
+ if (mConfig.idleTimerTimeout > 0ms) {
+ mIdleTimer.emplace(
+ "IdleTimer", mConfig.idleTimerTimeout,
+ [this] {
+ std::scoped_lock lock(mIdleTimerCallbacksMutex);
+ if (const auto callbacks = getIdleTimerCallbacks()) {
+ callbacks->onReset();
+ }
+ },
+ [this] {
+ std::scoped_lock lock(mIdleTimerCallbacksMutex);
+ if (const auto callbacks = getIdleTimerCallbacks()) {
+ callbacks->onExpired();
+ }
+ });
+ }
+}
+
+void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
+ std::lock_guard lock(mLock);
+
+ // Invalidate the cached invocation to getRankedFrameRates. This forces
+ // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+ mGetRankedFrameRatesCache.reset();
+
+ mDisplayModes = std::move(modes);
+ const auto activeModeOpt = mDisplayModes.get(activeModeId);
+ LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+ mActiveModeOpt =
+ FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())};
+
+ const auto sortedModes = sortByRefreshRate(mDisplayModes);
+ mMinRefreshRateModeIt = sortedModes.front();
+ mMaxRefreshRateModeIt = sortedModes.back();
+
+ // Reset the policy because the old one may no longer be valid.
+ mDisplayManagerPolicy = {};
+ mDisplayManagerPolicy.defaultMode = activeModeId;
+
+ mFrameRateOverrideConfig = [&] {
+ switch (mConfig.enableFrameRateOverride) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverride:
+ case Config::FrameRateOverride::Enabled:
+ return mConfig.enableFrameRateOverride;
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ return shouldEnableFrameRateOverride(sortedModes)
+ ? Config::FrameRateOverride::AppOverrideNativeRefreshRates
+ : Config::FrameRateOverride::Disabled;
+ }
+ }();
+
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates) {
+ for (const auto& [_, mode] : mDisplayModes) {
+ mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit);
+ }
+ }
+
+ constructAvailableRefreshRates();
+}
+
+bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const {
+ // defaultMode must be a valid mode, and within the given refresh rate range.
+ if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
+ if (!policy.primaryRanges.physical.includes(mode->get()->getFps())) {
+ ALOGE("Default mode is not in the primary range.");
+ return false;
+ }
+ } else {
+ ALOGE("Default mode is not found.");
+ return false;
+ }
+
+ const auto& primaryRanges = policy.primaryRanges;
+ const auto& appRequestRanges = policy.appRequestRanges;
+ ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
+ "Physical range is invalid: primary: %s appRequest: %s",
+ to_string(primaryRanges.physical).c_str(),
+ to_string(appRequestRanges.physical).c_str());
+ ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render),
+ "Render range is invalid: primary: %s appRequest: %s",
+ to_string(primaryRanges.render).c_str(), to_string(appRequestRanges.render).c_str());
+
+ return primaryRanges.valid() && appRequestRanges.valid();
+}
+
+auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
+ Policy oldPolicy;
+ PhysicalDisplayId displayId;
+ {
+ std::lock_guard lock(mLock);
+ oldPolicy = *getCurrentPolicyLocked();
+
+ const bool valid = ftl::match(
+ policy,
+ [this](const auto& policy) {
+ ftl::FakeGuard guard(mLock);
+ if (!isPolicyValidLocked(policy)) {
+ ALOGE("Invalid policy: %s", policy.toString().c_str());
+ return false;
+ }
+
+ using T = std::decay_t<decltype(policy)>;
+
+ if constexpr (std::is_same_v<T, DisplayManagerPolicy>) {
+ mDisplayManagerPolicy = policy;
+ } else {
+ static_assert(std::is_same_v<T, OverridePolicy>);
+ mOverridePolicy = policy;
+ }
+ return true;
+ },
+ [this](NoOverridePolicy) {
+ ftl::FakeGuard guard(mLock);
+ mOverridePolicy.reset();
+ return true;
+ });
+
+ if (!valid) {
+ return SetPolicyResult::Invalid;
+ }
+
+ mGetRankedFrameRatesCache.reset();
+
+ if (*getCurrentPolicyLocked() == oldPolicy) {
+ return SetPolicyResult::Unchanged;
+ }
+ constructAvailableRefreshRates();
+
+ displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId();
+ }
+
+ const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u);
+
+ ALOGI("Display %s policy changed\n"
+ "Previous: %s\n"
+ "Current: %s\n"
+ "%u mode changes were performed under the previous policy",
+ to_string(displayId).c_str(), oldPolicy.toString().c_str(), toString(policy).c_str(),
+ numModeChanges);
+
+ return SetPolicyResult::Changed;
+}
+
+auto RefreshRateSelector::getCurrentPolicyLocked() const -> const Policy* {
+ return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
+}
+
+auto RefreshRateSelector::getCurrentPolicy() const -> Policy {
+ std::lock_guard lock(mLock);
+ return *getCurrentPolicyLocked();
+}
+
+auto RefreshRateSelector::getDisplayManagerPolicy() const -> Policy {
+ std::lock_guard lock(mLock);
+ return mDisplayManagerPolicy;
+}
+
+bool RefreshRateSelector::isModeAllowed(const FrameRateMode& mode) const {
+ std::lock_guard lock(mLock);
+ return std::find(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(), mode) !=
+ mAppRequestFrameRates.end();
+}
+
+void RefreshRateSelector::constructAvailableRefreshRates() {
+ // Filter modes based on current policy and sort on refresh rate.
+ const Policy* policy = getCurrentPolicyLocked();
+ ALOGV("%s: %s ", __func__, policy->toString().c_str());
+
+ const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
+
+ const auto filterRefreshRates = [&](const FpsRanges& ranges,
+ const char* rangeName) REQUIRES(mLock) {
+ const auto filterModes = [&](const DisplayMode& mode) {
+ return mode.getResolution() == defaultMode->getResolution() &&
+ mode.getDpi() == defaultMode->getDpi() &&
+ (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
+ ranges.physical.includes(mode.getFps()) &&
+ (supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
+ };
+
+ const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
+ "No matching frame rate modes for %s range. policy: %s", rangeName,
+ policy->toString().c_str());
+
+ const auto stringifyModes = [&] {
+ std::string str;
+ for (const auto& frameRateMode : frameRateModes) {
+ str += to_string(frameRateMode) + " ";
+ }
+ return str;
+ };
+ ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str());
+
+ return frameRateModes;
+ };
+
+ mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
+ mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
+}
+
+Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
+ using namespace fps_approx_ops;
+
+ if (frameRate <= mKnownFrameRates.front()) {
+ return mKnownFrameRates.front();
+ }
+
+ if (frameRate >= mKnownFrameRates.back()) {
+ return mKnownFrameRates.back();
+ }
+
+ auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
+ isStrictlyLess);
+
+ const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
+ const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
+ return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
+}
+
+auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
+ std::lock_guard lock(mLock);
+
+ const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
+ const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked();
+
+ // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
+ // the min allowed refresh rate is higher than the device min, we do not want to enable the
+ // timer.
+ if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
+ return KernelIdleTimerAction::TurnOff;
+ }
+
+ const DisplayModePtr& maxByPolicy =
+ getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup());
+ if (minByPolicy == maxByPolicy) {
+ // Turn on the timer when the min of the primary range is below the device min.
+ if (const Policy* currentPolicy = getCurrentPolicyLocked();
+ isApproxLess(currentPolicy->primaryRanges.physical.min, deviceMinFps)) {
+ return KernelIdleTimerAction::TurnOn;
+ }
+ return KernelIdleTimerAction::TurnOff;
+ }
+
+ // Turn on the timer in all other cases.
+ return KernelIdleTimerAction::TurnOn;
+}
+
+int RefreshRateSelector::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
+ // This calculation needs to be in sync with the java code
+ // in DisplayManagerService.getDisplayInfoForFrameRateOverride
+
+ // The threshold must be smaller than 0.001 in order to differentiate
+ // between the fractional pairs (e.g. 59.94 and 60).
+ constexpr float kThreshold = 0.0009f;
+ const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue();
+ const auto numPeriodsRounded = std::round(numPeriods);
+ if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+ return 0;
+ }
+
+ return static_cast<int>(numPeriodsRounded);
+}
+
+bool RefreshRateSelector::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+ if (isStrictlyLess(bigger, smaller)) {
+ return isFractionalPairOrMultiple(bigger, smaller);
+ }
+
+ const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+ constexpr float kCoef = 1000.f / 1001.f;
+ return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
+ isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
+}
+
+void RefreshRateSelector::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
+
+ std::lock_guard lock(mLock);
+
+ const auto activeMode = getActiveModeLocked();
+ dumper.dump("activeMode"sv, to_string(activeMode));
+
+ dumper.dump("displayModes"sv);
+ {
+ utils::Dumper::Indent indent(dumper);
+ for (const auto& [id, mode] : mDisplayModes) {
+ dumper.dump({}, to_string(*mode));
+ }
+ }
+
+ dumper.dump("displayManagerPolicy"sv, mDisplayManagerPolicy.toString());
+
+ if (const Policy& currentPolicy = *getCurrentPolicyLocked();
+ mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
+ dumper.dump("overridePolicy"sv, currentPolicy.toString());
+ }
+
+ dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig));
+
+ dumper.dump("idleTimer"sv);
+ {
+ utils::Dumper::Indent indent(dumper);
+ dumper.dump("interval"sv, mIdleTimer.transform(&OneShotTimer::interval));
+ dumper.dump("controller"sv,
+ mConfig.kernelIdleTimerController
+ .and_then(&ftl::enum_name<KernelIdleTimerController>)
+ .value_or("Platform"sv));
+ }
+}
+
+std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() {
+ return mConfig.idleTimerTimeout;
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
similarity index 61%
rename from services/surfaceflinger/Scheduler/RefreshRateConfigs.h
rename to services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 0642fcb..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -18,13 +18,17 @@
#include <algorithm>
#include <numeric>
-#include <optional>
#include <type_traits>
#include <utility>
+#include <variant>
+#include <ftl/concat.h>
+#include <ftl/optional.h>
+#include <ftl/unit.h>
#include <gui/DisplayEventReceiver.h>
#include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/Seamlessness.h>
#include "DisplayHardware/DisplayMode.h"
@@ -32,6 +36,7 @@
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/StrongTyping.h"
#include "ThreadContext.h"
+#include "Utils/Dumper.h"
namespace android::scheduler {
@@ -44,30 +49,21 @@
return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
-struct RefreshRateRanking {
- DisplayModePtr displayModePtr;
- float score = 0.0f;
-
- bool operator==(const RefreshRateRanking& ranking) const {
- return displayModePtr == ranking.displayModePtr && score == ranking.score;
- }
-};
-
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
-/**
- * This class is used to encapsulate configuration for refresh rates. It holds information
- * about available refresh rates on the device, and the mapping between the numbers and human
- * readable names.
- */
-class RefreshRateConfigs {
+// Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with
+// the DisplayManager (or override) `Policy`, the `LayerRequirement` of each active layer,
+// and `GlobalSignals`.
+class RefreshRateSelector {
public:
// Margin used when matching refresh rates to the content desired ones.
static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
std::chrono::nanoseconds(800us).count();
- struct Policy {
- private:
+ // The lowest Render Frame Rate that will ever be selected
+ static constexpr Fps kMinSupportedFrameRate = 20_Hz;
+
+ class Policy {
static constexpr int kAllowGroupSwitchingDefault = false;
public:
@@ -76,40 +72,31 @@
DisplayModeId defaultMode;
// Whether or not we switch mode groups to get the best frame rate.
bool allowGroupSwitching = kAllowGroupSwitchingDefault;
- // The primary refresh rate range represents display manager's general guidance on the
- // display modes we'll consider when switching refresh rates. Unless we get an explicit
- // signal from an app, we should stay within this range.
- FpsRange primaryRange;
- // The app request refresh rate range allows us to consider more display modes when
- // switching refresh rates. Although we should generally stay within the primary range,
- // specific considerations, such as layer frame rate settings specified via the
- // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
- // app request range. The app request range will be greater than or equal to the primary
- // refresh rate range, never smaller.
- FpsRange appRequestRange;
+ // The primary refresh rate ranges. @see DisplayModeSpecs.aidl for details.
+ // TODO(b/257072060): use the render range when selecting SF render rate
+ // or the app override frame rate
+ FpsRanges primaryRanges;
+ // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details.
+ FpsRanges appRequestRanges;
Policy() = default;
- Policy(DisplayModeId defaultMode, FpsRange range)
- : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
+ Policy(DisplayModeId defaultMode, FpsRange range,
+ bool allowGroupSwitching = kAllowGroupSwitchingDefault)
+ : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range},
+ allowGroupSwitching) {}
- Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range)
- : Policy(defaultMode, allowGroupSwitching, range, range) {}
-
- Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange)
- : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
-
- Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange,
- FpsRange appRequestRange)
+ Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges,
+ bool allowGroupSwitching = kAllowGroupSwitchingDefault)
: defaultMode(defaultMode),
allowGroupSwitching(allowGroupSwitching),
- primaryRange(primaryRange),
- appRequestRange(appRequestRange) {}
+ primaryRanges(primaryRanges),
+ appRequestRanges(appRequestRanges) {}
bool operator==(const Policy& other) const {
using namespace fps_approx_ops;
- return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
- appRequestRange == other.appRequestRange &&
+ return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges &&
+ appRequestRanges == other.appRequestRanges &&
allowGroupSwitching == other.allowGroupSwitching;
}
@@ -117,23 +104,28 @@
std::string toString() const;
};
- // Return code set*Policy() to indicate the current policy is unchanged.
- static constexpr int CURRENT_POLICY_UNCHANGED = 1;
+ enum class SetPolicyResult { Invalid, Unchanged, Changed };
// We maintain the display manager policy and the override policy separately. The override
// policy is used by CTS tests to get a consistent device state for testing. While the override
// policy is set, it takes precedence over the display manager policy. Once the override policy
// is cleared, we revert to using the display manager policy.
+ struct DisplayManagerPolicy : Policy {
+ using Policy::Policy;
+ };
- // Sets the display manager policy to choose refresh rates. The return value will be:
- // - A negative value if the policy is invalid or another error occurred.
- // - NO_ERROR if the policy was successfully updated, and the current policy is different from
- // what it was before the call.
- // - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
- // is the same as it was before the call.
- status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
- // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
- status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
+ struct OverridePolicy : Policy {
+ using Policy::Policy;
+ };
+
+ struct NoOverridePolicy {};
+
+ using PolicyVariant = std::variant<DisplayManagerPolicy, OverridePolicy, NoOverridePolicy>;
+
+ SetPolicyResult setPolicy(const PolicyVariant&) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+
+ void onModeChangeInitiated() REQUIRES(kMainThreadContext) { mNumModeSwitchesInPolicy++; }
+
// Gets the current policy, which will be the override policy if active, and the display manager
// policy otherwise.
Policy getCurrentPolicy() const EXCLUDES(mLock);
@@ -141,7 +133,7 @@
Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
// Returns true if mode is allowed by the current policy.
- bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock);
+ bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock);
// Describes the different options the layer voted for refresh rate
enum class LayerVoteType {
@@ -202,26 +194,60 @@
return touch == other.touch && idle == other.idle &&
powerOnImminent == other.powerOnImminent;
}
+
+ auto toString() const {
+ return ftl::Concat("{touch=", touch, ", idle=", idle,
+ ", powerOnImminent=", powerOnImminent, '}');
+ }
};
- // Returns the list in the descending order of refresh rates desired
- // based on their overall score, and the GlobalSignals that were considered.
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRates(
- const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock);
+ struct ScoredFrameRate {
+ FrameRateMode frameRateMode;
+ float score = 0.0f;
+
+ bool operator==(const ScoredFrameRate& other) const {
+ return frameRateMode == other.frameRateMode && score == other.score;
+ }
+
+ static bool scoresEqual(float lhs, float rhs) {
+ constexpr float kEpsilon = 0.0001f;
+ return std::abs(lhs - rhs) <= kEpsilon;
+ }
+
+ struct DescendingScore {
+ bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const {
+ return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
+ }
+ };
+ };
+
+ using FrameRateRanking = std::vector<ScoredFrameRate>;
+
+ struct RankedFrameRates {
+ FrameRateRanking ranking; // Ordered by descending score.
+ GlobalSignals consideredSignals;
+
+ bool operator==(const RankedFrameRates& other) const {
+ return ranking == other.ranking && consideredSignals == other.consideredSignals;
+ }
+ };
+
+ RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
+ EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
}
- std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
- bool timerExpired) const EXCLUDES(mLock);
+ ftl::Optional<FrameRateMode> onKernelTimerChanged(
+ std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+ EXCLUDES(mLock);
- void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+ void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
- // See mActiveModeIt for thread safety.
- DisplayModePtr getActiveModePtr() const EXCLUDES(mLock);
- const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext);
+ // See mActiveModeOpt for thread safety.
+ FrameRateMode getActiveMode() const EXCLUDES(mLock);
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
@@ -230,7 +256,23 @@
// Configuration flags.
struct Config {
- bool enableFrameRateOverride = false;
+ enum class FrameRateOverride {
+ // Do not override the frame rate for an app
+ Disabled,
+
+ // Override the frame rate for an app to a value which is also
+ // a display refresh rate
+ AppOverrideNativeRefreshRates,
+
+ // Override the frame rate for an app to any value
+ AppOverride,
+
+ // Override the frame rate for all apps and all values.
+ Enabled,
+
+ ftl_last = Enabled
+ };
+ FrameRateOverride enableFrameRateOverride = FrameRateOverride::Disabled;
// Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
// or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
@@ -242,37 +284,47 @@
// The controller representing how the kernel idle timer will be configured
// either on the HWC api or sysprop.
- std::optional<KernelIdleTimerController> kernelIdleTimerController;
+ ftl::Optional<KernelIdleTimerController> kernelIdleTimerController;
};
- RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId,
- Config config = {.enableFrameRateOverride = false,
- .frameRateMultipleThreshold = 0,
- .idleTimerTimeout = 0ms,
- .kernelIdleTimerController = {}});
+ RefreshRateSelector(
+ DisplayModes, DisplayModeId activeModeId,
+ Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled,
+ .frameRateMultipleThreshold = 0,
+ .idleTimerTimeout = 0ms,
+ .kernelIdleTimerController = {}});
- RefreshRateConfigs(const RefreshRateConfigs&) = delete;
- RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete;
+ RefreshRateSelector(const RefreshRateSelector&) = delete;
+ RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
// Returns whether switching modes (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
- // differ in resolution.
+ // differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default,
+ // we can probably remove canSwitch altogether since all devices will be able
+ // to switch to a frame rate divisor.
bool canSwitch() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
- return mDisplayModes.size() > 1;
+ return mDisplayModes.size() > 1 ||
+ mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
}
// Class to enumerate options around toggling the kernel timer on and off.
enum class KernelIdleTimerAction {
- TurnOff, // Turn off the idle timer.
- TurnOn // Turn on the idle timer.
+ TurnOff, // Turn off the idle timer.
+ TurnOn // Turn on the idle timer.
};
// Checks whether kernel idle timer should be active depending the policy decisions around
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
- bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
+ bool supportsAppFrameRateOverrideByContent() const {
+ return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled;
+ }
+
+ bool supportsFrameRateOverride() const {
+ return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
+ }
// Return the display refresh rate divisor to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
@@ -326,30 +378,32 @@
}
}
- void resetIdleTimer(bool kernelOnly) {
- if (!mIdleTimer) {
- return;
+ void resetKernelIdleTimer() {
+ if (mIdleTimer && mConfig.kernelIdleTimerController) {
+ mIdleTimer->reset();
}
- if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) {
- return;
- }
- mIdleTimer->reset();
}
- void dump(std::string& result) const EXCLUDES(mLock);
+ void resetIdleTimer() {
+ if (mIdleTimer) {
+ mIdleTimer->reset();
+ }
+ }
+
+ void dump(utils::Dumper&) const EXCLUDES(mLock);
std::chrono::milliseconds getIdleTimerTimeout();
private:
- friend struct TestableRefreshRateConfigs;
+ friend struct TestableRefreshRateSelector;
void constructAvailableRefreshRates() REQUIRES(mLock);
- // See mActiveModeIt for thread safety.
- DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
+ // See mActiveModeOpt for thread safety.
+ const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedRefreshRatesLocked(
- const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
+ RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const REQUIRES(mLock);
// Returns number of display frames and remainder when dividing the layer refresh period by
// display refresh period.
@@ -365,19 +419,24 @@
struct RefreshRateScoreComparator;
- enum class RefreshRateOrder { Ascending, Descending };
+ enum class RefreshRateOrder {
+ Ascending,
+ Descending,
- // Returns the rankings in RefreshRateOrder. May change at runtime.
+ ftl_last = Descending
+ };
+
// Only uses the primary range, not the app request range.
- std::vector<RefreshRateRanking> getRefreshRatesByPolicyLocked(std::optional<int> anchorGroupOpt,
- RefreshRateOrder) const
+ FrameRateRanking rankFrameRates(
+ std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
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 calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock);
+ float calculateDistanceScoreFromMax(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,
@@ -398,25 +457,41 @@
: mIdleTimerCallbacks->platform;
}
+ bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) {
+ LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride !=
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+ "should only be called when "
+ "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used");
+ return mAppOverrideNativeRefreshRates.contains(fps);
+ }
+
+ std::vector<FrameRateMode> createFrameRateModes(
+ std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
+ REQUIRES(mLock);
+
// The display modes of the active display. The DisplayModeIterators below are pointers into
// this container, so must be invalidated whenever the DisplayModes change. The Policy below
// is also dependent, so must be reset as well.
DisplayModes mDisplayModes GUARDED_BY(mLock);
- // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
- // need not be under mLock.
- DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
+ // Set of supported display refresh rates for easy lookup
+ // when FrameRateOverride::AppOverrideNativeRefreshRates is in use.
+ ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
+
+ ftl::Optional<FrameRateMode> mActiveModeOpt GUARDED_BY(mLock);
DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
// Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
- std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
- std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+ unsigned mNumModeSwitchesInPolicy GUARDED_BY(kMainThreadContext) = 0;
+
mutable std::mutex mLock;
// A sorted list of known frame rates that a Heuristic layer will choose
@@ -424,19 +499,19 @@
const std::vector<Fps> mKnownFrameRates;
const Config mConfig;
- bool mSupportsFrameRateOverrideByContent;
+ Config::FrameRateOverride mFrameRateOverrideConfig;
- struct GetRankedRefreshRatesCache {
+ struct GetRankedFrameRatesCache {
std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> result;
+ RankedFrameRates result;
};
- mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
+ mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
// Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
std::mutex mIdleTimerCallbacksMutex;
std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
// Used to detect (lack of) frame activity.
- std::optional<scheduler::OneShotTimer> mIdleTimer;
+ ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6d68bac..34f8df2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,7 +25,9 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <ftl/enum.h>
#include <ftl/fake_guard.h>
+#include <ftl/small_map.h>
#include <gui/WindowInfo.h>
#include <system/window.h>
#include <utils/Timers.h>
@@ -41,9 +43,9 @@
#include "../Layer.h"
#include "DispSyncSource.h"
+#include "Display/DisplayMap.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
-#include "InjectVSyncSource.h"
#include "OneShotTimer.h"
#include "SurfaceFlingerProperties.h"
#include "VSyncPredictor.h"
@@ -67,8 +69,8 @@
mDisplayPowerTimer.reset();
mTouchTimer.reset();
- // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler.
- setRefreshRateConfigs(nullptr);
+ // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
+ demoteLeaderDisplay();
}
void Scheduler::startTimers() {
@@ -93,36 +95,29 @@
}
}
-void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
- // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
- {
- // mRefreshRateConfigsLock is not locked here to avoid the deadlock
- // as the callback can attempt to acquire the lock before stopIdleTimer can finish
- // the execution. It's safe to FakeGuard as main thread is the only thread that
- // writes to the mRefreshRateConfigs.
- ftl::FakeGuard guard(mRefreshRateConfigsLock);
- if (mRefreshRateConfigs) {
- mRefreshRateConfigs->stopIdleTimer();
- mRefreshRateConfigs->clearIdleTimerCallbacks();
- }
- }
- {
- // Clear state that depends on the current instance.
- std::scoped_lock lock(mPolicyLock);
- mPolicy = {};
- }
+void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+ demoteLeaderDisplay();
- std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs = std::move(configs);
- if (!mRefreshRateConfigs) return;
+ std::scoped_lock lock(mDisplayLock);
+ promoteLeaderDisplay(leaderIdOpt);
+}
- mRefreshRateConfigs->setIdleTimerCallbacks(
- {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
- .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+ demoteLeaderDisplay();
- mRefreshRateConfigs->startIdleTimer();
+ std::scoped_lock lock(mDisplayLock);
+ mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+
+ promoteLeaderDisplay();
+}
+
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+ demoteLeaderDisplay();
+
+ std::scoped_lock lock(mDisplayLock);
+ mRefreshRateSelectors.erase(displayId);
+
+ promoteLeaderDisplay();
}
void Scheduler::run() {
@@ -156,9 +151,8 @@
}
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
- const auto refreshRateConfigs = holdRefreshRateConfigs();
const bool supportsFrameRateOverrideByContent =
- refreshRateConfigs->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -173,8 +167,6 @@
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- std::scoped_lock lock(mRefreshRateConfigsLock);
-
return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
};
@@ -182,7 +174,7 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = holdRefreshRateConfigs()->getActiveModePtr()->getFps();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps;
const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
const auto frameRate = getFrameRateOverride(uid);
@@ -190,7 +182,7 @@
return currentPeriod;
}
- const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate);
+ const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
if (divisor <= 1) {
return currentPeriod;
}
@@ -198,15 +190,14 @@
};
}
-ConnectionHandle Scheduler::createConnection(
- const char* connectionName, frametimeline::TokenManager* tokenManager,
- std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
- impl::EventThread::InterceptVSyncsCallback interceptCallback) {
+ConnectionHandle Scheduler::createConnection(const char* connectionName,
+ frametimeline::TokenManager* tokenManager,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
auto throttleVsync = makeThrottleVsyncCallback();
auto getVsyncPeriod = makeGetVsyncPeriodFunction();
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
- std::move(interceptCallback),
std::move(throttleVsync),
std::move(getVsyncPeriod));
return createConnection(std::move(eventThread));
@@ -276,9 +267,8 @@
}
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
- const auto refreshRateConfigs = holdRefreshRateConfigs();
const bool supportsFrameRateOverrideByContent =
- refreshRateConfigs->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -292,7 +282,7 @@
thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
}
-void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
// Cache the last reported modes for primary display.
@@ -307,7 +297,7 @@
void Scheduler::dispatchCachedReportedMode() {
// Check optional fields first.
- if (!mPolicy.mode) {
+ if (!mPolicy.modeOpt) {
ALOGW("No mode ID found, not dispatching cached mode.");
return;
}
@@ -319,22 +309,21 @@
// If the mode is not the current mode, this means that a
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
- if (std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs->getActiveModePtr() != mPolicy.mode) {
+ if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
return;
}
// If there is no change from cached mode, there is no need to dispatch an event
- if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
+ if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
return;
}
- mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+ mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
mPolicy.cachedModeChangedParams->mode);
}
-void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -371,44 +360,6 @@
thread->setDuration(workDuration, readyDuration);
}
-ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
- if (mInjectVSyncs == enable) {
- return {};
- }
-
- ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
-
- if (!mInjectorConnectionHandle) {
- auto vsyncSource = std::make_unique<InjectVSyncSource>();
- mVSyncInjector = vsyncSource.get();
-
- auto eventThread =
- std::make_unique<impl::EventThread>(std::move(vsyncSource),
- /*tokenManager=*/nullptr,
- impl::EventThread::InterceptVSyncsCallback(),
- impl::EventThread::ThrottleVsyncCallback(),
- impl::EventThread::GetVsyncPeriodFunction());
-
- // EventThread does not dispatch VSYNC unless the display is connected and powered on.
- eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
- eventThread->onScreenAcquired();
-
- mInjectorConnectionHandle = createConnection(std::move(eventThread));
- }
-
- mInjectVSyncs = enable;
- return mInjectorConnectionHandle;
-}
-
-bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) {
- if (!mInjectVSyncs || !mVSyncInjector) {
- return false;
- }
-
- mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp);
- return true;
-}
-
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
@@ -444,6 +395,24 @@
setVsyncPeriod(refreshRate.getPeriodNsecs());
}
+void Scheduler::setRenderRate(Fps renderFrameRate) {
+ const auto mode = leaderSelectorPtr()->getActiveMode();
+
+ using fps_approx_ops::operator!=;
+ LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
+ "Mismatch in render frame rates. Selector: %s, Scheduler: %s",
+ to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str());
+
+ ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
+ to_string(mode.modePtr->getFps()).c_str());
+
+ const auto divisor = RefreshRateSelector::getFrameRateDivisor(mode.modePtr->getFps(), mode.fps);
+ LOG_ALWAYS_FATAL_IF(divisor == 0, "%s <> %s -- not divisors", to_string(mode.fps).c_str(),
+ to_string(mode.fps).c_str());
+
+ mVsyncSchedule->getTracker().setDivisor(static_cast<unsigned>(divisor));
+}
+
void Scheduler::resync() {
static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
@@ -451,10 +420,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- const auto refreshRate = [&] {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- return mRefreshRateConfigs->getActiveModePtr()->getFps();
- }();
+ const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
resyncToHardwareVsync(false, refreshRate);
}
}
@@ -513,12 +479,9 @@
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
- {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- if (!mRefreshRateConfigs->canSwitch()) return;
+ if (leaderSelectorPtr()->canSwitch()) {
+ mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
-
- mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
void Scheduler::setModeChangePending(bool pending) {
@@ -531,26 +494,23 @@
}
void Scheduler::chooseRefreshRateForContent() {
- const auto configs = holdRefreshRateConfigs();
- if (!configs->canSwitch()) return;
+ const auto selectorPtr = leaderSelectorPtr();
+ if (!selectorPtr->canSwitch()) return;
ATRACE_CALL();
- LayerHistory::Summary summary = mLayerHistory.summarize(*configs, systemTime());
+ LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
applyPolicy(&Policy::contentRequirements, std::move(summary));
}
void Scheduler::resetIdleTimer() {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
+ leaderSelectorPtr()->resetIdleTimer();
}
void Scheduler::onTouchHint() {
if (mTouchTimer) {
mTouchTimer->reset();
-
- std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true);
+ leaderSelectorPtr()->resetKernelIdleTimer();
}
}
@@ -575,10 +535,7 @@
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const Fps refreshRate = [&] {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- return mRefreshRateConfigs->getActiveModePtr()->getFps();
- }();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
using namespace fps_approx_ops;
@@ -620,22 +577,40 @@
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
-void Scheduler::dump(std::string& result) const {
- using base::StringAppendF;
-
- StringAppendF(&result, "+ Touch timer: %s\n",
- mTouchTimer ? mTouchTimer->dump().c_str() : "off");
- StringAppendF(&result, "+ Content detection: %s %s\n\n",
- mFeatures.test(Feature::kContentDetection) ? "on" : "off",
- mLayerHistory.dump().c_str());
-
- mFrameRateOverrideMappings.dump(result);
+void Scheduler::dump(utils::Dumper& dumper) const {
+ using namespace std::string_view_literals;
{
+ utils::Dumper::Section section(dumper, "Features"sv);
+
+ for (Feature feature : ftl::enum_range<Feature>()) {
+ if (const auto flagOpt = ftl::flag_name(feature)) {
+ dumper.dump(flagOpt->substr(1), mFeatures.test(feature));
+ }
+ }
+ }
+ {
+ utils::Dumper::Section section(dumper, "Policy"sv);
+ {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+ dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+ }
+ dumper.dump("layerHistory"sv, mLayerHistory.dump());
+ dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
+ dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
+ }
+
+ mFrameRateOverrideMappings.dump(dumper);
+ dumper.eol();
+
+ {
+ utils::Dumper::Section section(dumper, "Hardware VSYNC"sv);
+
std::lock_guard lock(mHWVsyncLock);
- StringAppendF(&result,
- "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n",
- mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable);
+ dumper.dump("screenAcquired"sv, mScreenAcquired.load());
+ dumper.dump("hwVsyncAvailable"sv, mHWVsyncAvailable);
+ dumper.dump("hwVsyncEnabled"sv, mPrimaryHWVsyncEnabled);
}
}
@@ -644,56 +619,101 @@
}
bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
- const auto refreshRateConfigs = holdRefreshRateConfigs();
+ if (consideredSignals.idle) return false;
- // we always update mFrameRateOverridesByContent here
- // supportsFrameRateOverridesByContent will be checked
- // when getting FrameRateOverrides from mFrameRateOverrideMappings
- if (!consideredSignals.idle) {
- const auto frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
- displayRefreshRate, consideredSignals);
- return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+ const auto frameRateOverrides =
+ leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+ displayRefreshRate, consideredSignals);
+
+ // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
+ // the FrameRateOverrideMappings rather than here.
+ return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+}
+
+void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+ // TODO(b/241286431): Choose the leader display.
+ mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+ ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+
+ if (const auto leaderPtr = leaderSelectorPtrLocked()) {
+ leaderPtr->setIdleTimerCallbacks(
+ {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+ .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ .onExpired =
+ [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+ leaderPtr->startIdleTimer();
}
- return false;
+}
+
+void Scheduler::demoteLeaderDisplay() {
+ // No need to lock for reads on kMainThreadContext.
+ if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
+ leaderPtr->stopIdleTimer();
+ leaderPtr->clearIdleTimerCallbacks();
+ }
+
+ // Clear state that depends on the leader's RefreshRateSelector.
+ std::scoped_lock lock(mPolicyLock);
+ mPolicy = {};
}
template <typename S, typename T>
auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
- DisplayModePtr newMode;
+ std::vector<display::DisplayModeRequest> modeRequests;
GlobalSignals consideredSignals;
bool refreshRateChanged = false;
bool frameRateOverridesChanged;
- const auto refreshRateConfigs = holdRefreshRateConfigs();
{
- std::lock_guard<std::mutex> lock(mPolicyLock);
+ std::scoped_lock lock(mPolicyLock);
auto& currentState = mPolicy.*statePtr;
if (currentState == newState) return {};
currentState = std::forward<T>(newState);
- const auto [rankings, signals] = getRankedDisplayModes();
- newMode = rankings.front().displayModePtr;
- consideredSignals = signals;
- frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
+ DisplayModeChoiceMap modeChoices;
+ ftl::Optional<FrameRateMode> modeOpt;
+ {
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
- if (mPolicy.mode == newMode) {
+ modeChoices = chooseDisplayModes();
+
+ // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
+ // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+ std::tie(modeOpt, consideredSignals) =
+ modeChoices.get(*mLeaderDisplayId)
+ .transform([](const DisplayModeChoice& choice) {
+ return std::make_pair(choice.mode, choice.consideredSignals);
+ })
+ .value();
+ }
+
+ modeRequests.reserve(modeChoices.size());
+ for (auto& [id, choice] : modeChoices) {
+ modeRequests.emplace_back(
+ display::DisplayModeRequest{.mode = std::move(choice.mode),
+ .emitEvent = !choice.consideredSignals.idle});
+ }
+
+ frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps);
+
+ if (mPolicy.modeOpt != modeOpt) {
+ mPolicy.modeOpt = modeOpt;
+ refreshRateChanged = true;
+ } else {
// We don't need to change the display mode, but we might need to send an event
// about a mode change, since it was suppressed if previously considered idle.
if (!consideredSignals.idle) {
dispatchCachedReportedMode();
}
- } else {
- mPolicy.mode = newMode;
- refreshRateChanged = true;
}
}
if (refreshRateChanged) {
- mSchedulerCallback.requestDisplayMode(std::move(newMode),
- consideredSignals.idle ? DisplayModeEvent::None
- : DisplayModeEvent::Changed);
+ mSchedulerCallback.requestDisplayModes(std::move(modeRequests));
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -701,30 +721,112 @@
return consideredSignals;
}
-auto Scheduler::getRankedDisplayModes()
- -> std::pair<std::vector<RefreshRateRanking>, GlobalSignals> {
+auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
ATRACE_CALL();
- const auto configs = holdRefreshRateConfigs();
+ using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
+ display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
+ // Tallies the score of a refresh rate across `displayCount` displays.
+ struct RefreshRateTally {
+ explicit RefreshRateTally(float score) : score(score) {}
+
+ float score;
+ size_t displayCount = 1;
+ };
+
+ // Chosen to exceed a typical number of refresh rates across displays.
+ constexpr size_t kStaticCapacity = 8;
+ ftl::SmallMap<Fps, RefreshRateTally, kStaticCapacity, FpsApproxEqual> refreshRateTallies;
+
+ const auto globalSignals = makeGlobalSignals();
+
+ for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
+ auto rankedFrameRates =
+ selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
+
+ for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
+ const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);
+
+ if (!inserted) {
+ auto& tally = it->second;
+ tally.score += score;
+ tally.displayCount++;
+ }
+ }
+
+ perDisplayRanking.push_back(std::move(rankedFrameRates));
+ }
+
+ auto maxScoreIt = refreshRateTallies.cbegin();
+
+ // Find the first refresh rate common to all displays.
+ while (maxScoreIt != refreshRateTallies.cend() &&
+ maxScoreIt->second.displayCount != mRefreshRateSelectors.size()) {
+ ++maxScoreIt;
+ }
+
+ if (maxScoreIt != refreshRateTallies.cend()) {
+ // Choose the highest refresh rate common to all displays, if any.
+ for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) {
+ const auto [fps, tally] = *it;
+
+ if (tally.displayCount == mRefreshRateSelectors.size() &&
+ tally.score > maxScoreIt->second.score) {
+ maxScoreIt = it;
+ }
+ }
+ }
+
+ const std::optional<Fps> chosenFps = maxScoreIt != refreshRateTallies.cend()
+ ? std::make_optional(maxScoreIt->first)
+ : std::nullopt;
+
+ DisplayModeChoiceMap modeChoices;
+
+ using fps_approx_ops::operator==;
+
+ for (auto& [ranking, signals] : perDisplayRanking) {
+ if (!chosenFps) {
+ const auto& [frameRateMode, _] = ranking.front();
+ modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{frameRateMode, signals});
+ continue;
+ }
+
+ for (auto& [frameRateMode, _] : ranking) {
+ if (frameRateMode.fps == *chosenFps) {
+ modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{frameRateMode, signals});
+ break;
+ }
+ }
+ }
+ return modeChoices;
+}
+
+GlobalSignals Scheduler::makeGlobalSignals() const {
const bool powerOnImminent = mDisplayPowerTimer &&
(mPolicy.displayPowerMode != hal::PowerMode::ON ||
mPolicy.displayPowerTimer == TimerState::Reset);
- const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
- .idle = mPolicy.idleTimer == TimerState::Expired,
- .powerOnImminent = powerOnImminent};
-
- return configs->getRankedRefreshRates(mPolicy.contentRequirements, signals);
+ return {.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
+ .idle = mPolicy.idleTimer == TimerState::Expired,
+ .powerOnImminent = powerOnImminent};
}
-DisplayModePtr Scheduler::getPreferredDisplayMode() {
+ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
// Make sure the stored mode is up to date.
- if (mPolicy.mode) {
- mPolicy.mode = getRankedDisplayModes().first.front().displayModePtr;
+ if (mPolicy.modeOpt) {
+ const auto ranking =
+ leaderSelectorPtr()
+ ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
+ .ranking;
+
+ mPolicy.modeOpt = ranking.front().frameRateMode;
}
- return mPolicy.mode;
+ return mPolicy.modeOpt;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f567205..cf2ffb8 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -22,8 +22,8 @@
#include <future>
#include <memory>
#include <mutex>
-#include <optional>
#include <unordered_map>
+#include <utility>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -32,15 +32,21 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <ftl/fake_guard.h>
+#include <ftl/optional.h>
#include <scheduler/Features.h>
#include <scheduler/Time.h>
+#include <ui/DisplayId.h>
+#include "Display/DisplayMap.h"
+#include "Display/DisplayModeRequest.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "LayerHistory.h"
#include "MessageQueue.h"
#include "OneShotTimer.h"
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
+#include "Utils/Dumper.h"
#include "VsyncSchedule.h"
namespace android::scheduler {
@@ -75,7 +81,6 @@
namespace android {
class FenceTime;
-class InjectVSyncSource;
namespace frametimeline {
class TokenManager;
@@ -83,11 +88,11 @@
namespace scheduler {
-struct ISchedulerCallback {
- using DisplayModeEvent = scheduler::DisplayModeEvent;
+using GlobalSignals = RefreshRateSelector::GlobalSignals;
+struct ISchedulerCallback {
virtual void setVsyncEnabled(bool) = 0;
- virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0;
+ virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
@@ -103,15 +108,22 @@
virtual ~Scheduler();
void startTimers();
- void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>)
- EXCLUDES(mRefreshRateConfigsLock);
+
+ // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
+ void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
+
+ void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+ void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
void run();
void createVsyncSchedule(FeatureFlags);
using Impl::initVsync;
- using Impl::setInjector;
using Impl::getScheduledFrameTime;
using Impl::setDuration;
@@ -129,8 +141,7 @@
ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
std::chrono::nanoseconds workDuration,
- std::chrono::nanoseconds readyDuration,
- android::impl::EventThread::InterceptVSyncsCallback);
+ std::chrono::nanoseconds readyDuration);
sp<IDisplayEventConnection> createDisplayEventConnection(
ConnectionHandle, EventRegistrationFlags eventRegistration = {});
@@ -138,8 +149,8 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
- void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
+ void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -150,10 +161,9 @@
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
- // Returns injector handle if injection has toggled, or an invalid handle otherwise.
- ConnectionHandle enableVSyncInjection(bool enable);
- // Returns false if injection is disabled.
- bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp);
+ // Sets the render rate for the scheduler to run at.
+ void setRenderRate(Fps);
+
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
@@ -162,7 +172,7 @@
// Otherwise, if hardware vsync is not already enabled then this method will
// no-op.
void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
- void resync() EXCLUDES(mRefreshRateConfigsLock);
+ void resync() EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. periodFlushed will be true if
@@ -173,14 +183,14 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
- EXCLUDES(mRefreshRateConfigsLock);
+ void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
+ EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
// Detects content using layer history, and selects a matching refresh rate.
- void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock);
+ void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
void resetIdleTimer();
@@ -195,12 +205,12 @@
// for a given uid
bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
- void dump(std::string&) const;
+ void dump(utils::Dumper&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
// Get the appropriate refresh for current conditions.
- DisplayModePtr getPreferredDisplayMode();
+ ftl::Optional<FrameRateMode> getPreferredDisplayMode();
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -225,11 +235,10 @@
void setGameModeRefreshRateForUid(FrameRateOverride);
// Retrieves the overridden refresh rate for a given uid.
- std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock);
+ std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
- nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- return mRefreshRateConfigs->getActiveModePtr()->getFps().getPeriodNsecs();
+ nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
+ return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs();
}
// Returns the framerate of the layer with the given sequence ID
@@ -253,14 +262,21 @@
EventThread*, EventRegistrationFlags eventRegistration = {});
// Update feature state machine to given state when corresponding timer resets or expires.
- void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock);
+ void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
void idleTimerCallback(TimerState);
void touchTimerCallback(TimerState);
void displayPowerTimerCallback(TimerState);
void setVsyncPeriod(nsecs_t period);
- using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+ // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
+ // `mLeaderDisplayId` is never `std::nullopt`.
+ void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+ REQUIRES(kMainThreadContext, mDisplayLock);
+
+ // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
+ // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+ void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
struct Policy;
@@ -269,25 +285,39 @@
template <typename S, typename T>
GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
- // Returns the list of display modes in descending order of their priority that fulfills the
- // policy, and the signals that were considered.
- std::pair<std::vector<RefreshRateRanking>, GlobalSignals> getRankedDisplayModes()
- REQUIRES(mPolicyLock);
+ struct DisplayModeChoice {
+ DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
+ : mode(std::move(mode)), consideredSignals(consideredSignals) {}
+
+ FrameRateMode mode;
+ GlobalSignals consideredSignals;
+
+ bool operator==(const DisplayModeChoice& other) const {
+ return mode == other.mode && consideredSignals == other.consideredSignals;
+ }
+
+ // For tests.
+ friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) {
+ return stream << '{' << to_string(*choice.mode.modePtr) << " considering "
+ << choice.consideredSignals.toString().c_str() << '}';
+ }
+ };
+
+ using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
+
+ // See mDisplayLock for thread safety.
+ DisplayModeChoiceMap chooseDisplayModes() const
+ REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext);
+
+ GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
- void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
+ void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
- android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
- EXCLUDES(mRefreshRateConfigsLock);
+ android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
- std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
- EXCLUDES(mRefreshRateConfigsLock) {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- return mRefreshRateConfigs;
- }
-
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -298,10 +328,6 @@
mutable std::mutex mConnectionsLock;
std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
- bool mInjectVSyncs = false;
- InjectVSyncSource* mVSyncInjector = nullptr;
- ConnectionHandle mInjectorConnectionHandle;
-
mutable std::mutex mHWVsyncLock;
bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
@@ -315,14 +341,41 @@
LayerHistory mLayerHistory;
// Timer used to monitor touch events.
- std::optional<OneShotTimer> mTouchTimer;
+ ftl::Optional<OneShotTimer> mTouchTimer;
// Timer used to monitor display power mode.
- std::optional<OneShotTimer> mDisplayPowerTimer;
+ ftl::Optional<OneShotTimer> mDisplayPowerTimer;
ISchedulerCallback& mSchedulerCallback;
+ // mDisplayLock may be locked while under mPolicyLock.
mutable std::mutex mPolicyLock;
+ // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so
+ // must lock for writes but not reads. See also mPolicyLock for locking order.
+ mutable std::mutex mDisplayLock;
+
+ display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
+ GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+
+ ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+ GUARDED_BY(kMainThreadContext);
+
+ RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+ std::scoped_lock lock(mDisplayLock);
+ return leaderSelectorPtrLocked();
+ }
+
+ RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ const RefreshRateSelectorPtr noLeader;
+ return mLeaderDisplayId
+ .and_then([this](PhysicalDisplayId leaderId)
+ REQUIRES(mDisplayLock, kMainThreadContext) {
+ return mRefreshRateSelectors.get(leaderId);
+ })
+ .value_or(std::cref(noLeader));
+ }
+
struct Policy {
// Policy for choosing the display mode.
LayerHistory::Summary contentRequirements;
@@ -332,20 +385,17 @@
hal::PowerMode displayPowerMode = hal::PowerMode::ON;
// Chosen display mode.
- DisplayModePtr mode;
+ ftl::Optional<FrameRateMode> modeOpt;
struct ModeChangedParams {
ConnectionHandle handle;
- DisplayModePtr mode;
+ FrameRateMode mode;
};
// Parameters for latest dispatch of mode change event.
std::optional<ModeChangedParams> cachedModeChangedParams;
} mPolicy GUARDED_BY(mPolicyLock);
- mutable std::mutex mRefreshRateConfigsLock;
- std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock);
-
std::mutex mVsyncTimelineLock;
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 898e865..ed4d25e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -34,7 +34,7 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
#include "VSyncPredictor.h"
namespace android::scheduler {
@@ -253,7 +253,13 @@
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard lock(mMutex);
- return nextAnticipatedVSyncTimeFromLocked(timePoint);
+
+ // TODO(b/246164114): This implementation is not efficient at all. Refactor.
+ nsecs_t nextVsync = nextAnticipatedVSyncTimeFromLocked(timePoint);
+ while (!isVSyncInPhaseLocked(nextVsync, mDivisor)) {
+ nextVsync = nextAnticipatedVSyncTimeFromLocked(nextVsync + 1);
+ }
+ return nextVsync;
}
/*
@@ -265,6 +271,13 @@
* isVSyncInPhase(50.0, 30) = true
*/
bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
+ std::lock_guard lock(mMutex);
+ const auto divisor =
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+ return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
+}
+
+bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
struct VsyncError {
nsecs_t vsyncTimestamp;
float error;
@@ -272,9 +285,6 @@
bool operator<(const VsyncError& other) const { return error < other.error; }
};
- std::lock_guard lock(mMutex);
- const auto divisor =
- RefreshRateConfigs::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
if (divisor <= 1 || timePoint == 0) {
return true;
}
@@ -312,6 +322,12 @@
return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
}
+void VSyncPredictor::setDivisor(unsigned divisor) {
+ ALOGV("%s: %d", __func__, divisor);
+ std::lock_guard lock(mMutex);
+ mDivisor = divisor;
+}
+
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
std::lock_guard lock(mMutex);
const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3181102..4a3ba67 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -67,6 +67,8 @@
bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
+ void setDivisor(unsigned divisor) final EXCLUDES(mMutex);
+
void dump(std::string& result) const final EXCLUDES(mMutex);
private:
@@ -89,6 +91,8 @@
nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
+ bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
+
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
@@ -100,6 +104,8 @@
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
+
+ unsigned mDivisor GUARDED_BY(mMutex) = 1;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 76315d2..8d1629f 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -79,6 +79,17 @@
*/
virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
+ /*
+ * Sets a divisor on the rate (which is a multiplier of the period).
+ * The tracker will continue to track the vsync timeline and expect it
+ * to match the current period, however, nextAnticipatedVSyncTimeFrom will
+ * return vsyncs according to the divisor set. Setting a divisor is useful
+ * when a display is running at 120Hz but the render frame rate is 60Hz.
+ *
+ * \param [in] divisor The rate divisor the tracker should operate at.
+ */
+ virtual void setDivisor(unsigned divisor) = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index bd4f409..5522ff8 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -66,6 +66,18 @@
Fps max = Fps::fromValue(std::numeric_limits<float>::max());
bool includes(Fps) const;
+ bool includes(FpsRange) const;
+};
+
+struct FpsRanges {
+ // The range of refresh rates that refers to the display mode setting.
+ FpsRange physical;
+
+ // the range of frame rates that refers to the render rate, which is
+ // the rate that frames are swapped.
+ FpsRange render;
+
+ bool valid() const;
};
static_assert(std::is_trivially_copyable_v<Fps>);
@@ -127,13 +139,39 @@
return !(lhs == rhs);
}
+inline bool operator==(const FpsRanges& lhs, const FpsRanges& rhs) {
+ return lhs.physical == rhs.physical && lhs.render == rhs.render;
+}
+
+inline bool operator!=(const FpsRanges& lhs, const FpsRanges& rhs) {
+ return !(lhs == rhs);
+}
+
+inline unsigned operator/(Fps lhs, Fps rhs) {
+ return static_cast<unsigned>(std::ceil(lhs.getValue() / rhs.getValue()));
+}
+
} // namespace fps_approx_ops
+constexpr Fps operator/(Fps fps, unsigned divisor) {
+ return Fps::fromPeriodNsecs(fps.getPeriodNsecs() * static_cast<nsecs_t>(divisor));
+}
+
inline bool FpsRange::includes(Fps fps) const {
using fps_approx_ops::operator<=;
return min <= fps && fps <= max;
}
+inline bool FpsRange::includes(FpsRange range) const {
+ using namespace fps_approx_ops;
+ return min <= range.min && max >= range.max;
+}
+
+inline bool FpsRanges::valid() const {
+ using fps_approx_ops::operator>=;
+ return physical.max >= render.max;
+}
+
struct FpsApproxEqual {
bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
};
@@ -151,4 +189,10 @@
return base::StringPrintf("[%s, %s]", to_string(min).c_str(), to_string(max).c_str());
}
+inline std::string to_string(FpsRanges ranges) {
+ const auto& [physical, render] = ranges;
+ return base::StringPrintf("{physical=%s, render=%s}", to_string(physical).c_str(),
+ to_string(render).c_str());
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
new file mode 100644
index 0000000..db38ebe
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <ftl/non_null.h>
+#include <scheduler/Fps.h>
+
+// TODO(b/241285191): Pull this to <ui/DisplayMode.h>
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::scheduler {
+
+struct FrameRateMode {
+ Fps fps; // The render frame rate, which is a divisor of modePtr->getFps().
+ ftl::NonNull<DisplayModePtr> modePtr;
+
+ bool operator==(const FrameRateMode& other) const {
+ return isApproxEqual(fps, other.fps) && modePtr == other.modePtr;
+ }
+
+ bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+};
+
+inline std::string to_string(const FrameRateMode& mode) {
+ return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
index 2ca55d4..bd4e3c2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Time.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -17,7 +17,9 @@
#pragma once
#include <chrono>
+#include <string>
+#include <android-base/stringprintf.h>
#include <utils/Timers.h>
namespace android {
@@ -79,4 +81,8 @@
return std::chrono::duration_cast<D>(d).count();
}
+inline std::string to_string(Duration d) {
+ return base::StringPrintf("%.3f ms", ticks<std::milli, float>(d));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
new file mode 100644
index 0000000..37b3218
--- /dev/null
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "ScreenCaptureOutput.h"
+#include "ScreenCaptureRenderSurface.h"
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
+ std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
+ ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
+ std::unordered_set<compositionengine::LayerFE*>,
+ const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine,
+ args.renderArea,
+ std::move(
+ args.filterForScreenshot),
+ args.colorProfile,
+ args.regionSampling);
+ output->editState().isSecure = args.renderArea.isSecure();
+ output->setCompositionEnabled(true);
+ output->setLayerFilter({args.layerStack});
+ output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
+ output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits);
+
+ output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>(
+ compositionengine::DisplayColorProfileCreationArgsBuilder()
+ .setHasWideColorGamut(true)
+ .Build()));
+
+ ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags());
+ Rect orientedDisplaySpaceRect{args.renderArea.getReqWidth(), args.renderArea.getReqHeight()};
+ output->setProjection(orientation, args.renderArea.getLayerStackSpaceRect(),
+ orientedDisplaySpaceRect);
+
+ Rect sourceCrop = args.renderArea.getSourceCrop();
+ output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
+
+ return output;
+}
+
+ScreenCaptureOutput::ScreenCaptureOutput(
+ const RenderArea& renderArea,
+ std::unordered_set<compositionengine::LayerFE*> filterForScreenshot,
+ const compositionengine::Output::ColorProfile& colorProfile, bool regionSampling)
+ : mRenderArea(renderArea),
+ mFilterForScreenshot(std::move(filterForScreenshot)),
+ mColorProfile(colorProfile),
+ mRegionSampling(regionSampling) {}
+
+void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) {
+ auto& outputState = editState();
+ outputState.dataspace = mColorProfile.dataspace;
+ outputState.renderIntent = mColorProfile.renderIntent;
+}
+
+renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisplaySettings()
+ const {
+ auto clientCompositionDisplay =
+ compositionengine::impl::Output::generateClientCompositionDisplaySettings();
+ clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
+ clientCompositionDisplay.targetLuminanceNits = -1;
+ return clientCompositionDisplay;
+}
+
+std::vector<compositionengine::LayerFE::LayerSettings>
+ScreenCaptureOutput::generateClientCompositionRequests(
+ bool supportsProtectedContent, ui::Dataspace outputDataspace,
+ std::vector<compositionengine::LayerFE*>& outLayerFEs) {
+ auto clientCompositionLayers = compositionengine::impl::Output::
+ generateClientCompositionRequests(supportsProtectedContent, outputDataspace,
+ outLayerFEs);
+
+ if (mRegionSampling) {
+ for (auto& layer : clientCompositionLayers) {
+ layer.backgroundBlurRadius = 0;
+ layer.blurRegions.clear();
+ }
+ }
+
+ Rect sourceCrop = mRenderArea.getSourceCrop();
+ compositionengine::LayerFE::LayerSettings fillLayer;
+ fillLayer.source.buffer.buffer = nullptr;
+ fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+ fillLayer.geometry.boundaries =
+ FloatRect(static_cast<float>(sourceCrop.left), static_cast<float>(sourceCrop.top),
+ static_cast<float>(sourceCrop.right), static_cast<float>(sourceCrop.bottom));
+ fillLayer.alpha = half(RenderArea::getCaptureFillValue(mRenderArea.getCaptureFill()));
+ clientCompositionLayers.insert(clientCompositionLayers.begin(), fillLayer);
+
+ return clientCompositionLayers;
+}
+
+bool ScreenCaptureOutput::layerNeedsFiltering(const compositionengine::OutputLayer* layer) const {
+ return mRenderArea.needsFiltering() ||
+ mFilterForScreenshot.find(&layer->getLayerFE()) != mFilterForScreenshot.end();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
new file mode 100644
index 0000000..5dffc1d
--- /dev/null
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -0,0 +1,72 @@
+/*
+ * 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 <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/Rect.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+struct ScreenCaptureOutputArgs {
+ const compositionengine::CompositionEngine& compositionEngine;
+ const compositionengine::Output::ColorProfile& colorProfile;
+ const RenderArea& renderArea;
+ ui::LayerStack layerStack;
+ std::shared_ptr<renderengine::ExternalTexture> buffer;
+ float sdrWhitePointNits;
+ float displayBrightnessNits;
+ std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
+ bool regionSampling;
+};
+
+// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
+//
+// SurfaceFlinger passes instances of ScreenCaptureOutput to CompositionEngine in calls to
+// SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay.
+class ScreenCaptureOutput : public compositionengine::impl::Output {
+public:
+ ScreenCaptureOutput(const RenderArea& renderArea,
+ std::unordered_set<compositionengine::LayerFE*> filterForScreenshot,
+ const compositionengine::Output::ColorProfile& colorProfile,
+ bool regionSampling);
+
+ void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
+
+ std::vector<compositionengine::LayerFE::LayerSettings> generateClientCompositionRequests(
+ bool supportsProtectedContent, ui::Dataspace outputDataspace,
+ std::vector<compositionengine::LayerFE*>& outLayerFEs) override;
+
+ bool layerNeedsFiltering(const compositionengine::OutputLayer*) const override;
+
+protected:
+ bool getSkipColorTransform() const override { return false; }
+ renderengine::DisplaySettings generateClientCompositionDisplaySettings() const override;
+
+private:
+ const RenderArea& mRenderArea;
+ const std::unordered_set<compositionengine::LayerFE*> mFilterForScreenshot;
+ const compositionengine::Output::ColorProfile& mColorProfile;
+ const bool mRegionSampling;
+};
+
+std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs);
+
+} // namespace android
diff --git a/services/surfaceflinger/ScreenCaptureRenderSurface.h b/services/surfaceflinger/ScreenCaptureRenderSurface.h
new file mode 100644
index 0000000..2097300
--- /dev/null
+++ b/services/surfaceflinger/ScreenCaptureRenderSurface.h
@@ -0,0 +1,81 @@
+/*
+ * 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 <memory>
+
+#include <compositionengine/RenderSurface.h>
+#include <renderengine/impl/ExternalTexture.h>
+#include <ui/Fence.h>
+#include <ui/Size.h>
+
+namespace android {
+
+// ScreenCaptureRenderSurface is a RenderSurface that returns a preallocated buffer used by
+// ScreenCaptureOutput.
+class ScreenCaptureRenderSurface : public compositionengine::RenderSurface {
+public:
+ ScreenCaptureRenderSurface(std::shared_ptr<renderengine::ExternalTexture> buffer)
+ : mBuffer(std::move(buffer)){};
+
+ std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+ base::unique_fd* /* bufferFence */) override {
+ return mBuffer;
+ }
+
+ void queueBuffer(base::unique_fd readyFence) override {
+ mRenderFence = sp<Fence>::make(readyFence.release());
+ }
+
+ const sp<Fence>& getClientTargetAcquireFence() const override { return mRenderFence; }
+
+ bool supportsCompositionStrategyPrediction() const override { return false; }
+
+ bool isValid() const override { return true; }
+
+ void initialize() override {}
+
+ const ui::Size& getSize() const override { return mSize; }
+
+ bool isProtected() const override { return mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED; }
+
+ void setDisplaySize(const ui::Size&) override {}
+
+ void setBufferDataspace(ui::Dataspace) override {}
+
+ void setBufferPixelFormat(ui::PixelFormat) override {}
+
+ void setProtected(bool /* useProtected */) override {}
+
+ status_t beginFrame(bool /* mustRecompose */) override { return OK; }
+
+ void prepareFrame(bool /* usesClientComposition */, bool /* usesDeviceComposition */) override {
+ }
+
+ void onPresentDisplayCompleted() override {}
+
+ void dump(std::string& /* result */) const override {}
+
+private:
+ std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+
+ sp<Fence> mRenderFence = Fence::NO_FENCE;
+
+ ui::Size mSize;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7d3fc98..de94e45 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -44,16 +44,19 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <configstore/Utils.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <ftl/algorithm.h>
+#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
#include <ftl/unit.h>
@@ -84,6 +87,7 @@
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicBufferAllocator.h>
+#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
#include <utils/StopWatch.h>
@@ -103,10 +107,12 @@
#include <optional>
#include <type_traits>
#include <unordered_map>
+#include <vector>
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
+#include "ClientCache.h"
#include "Colorizer.h"
#include "Display/DisplayMap.h"
#include "DisplayDevice.h"
@@ -122,6 +128,8 @@
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "HdrLayerInfoReporter.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
@@ -129,19 +137,17 @@
#include "LayerVector.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
-#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
-#include "Scheduler/VsyncController.h"
+#include "ScreenCaptureOutput.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
+#include "Utils/Dumper.h"
#include "WindowInfosListenerInvoker.h"
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
@@ -160,6 +166,7 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
+using namespace std::string_view_literals;
using namespace hardware::configstore;
using namespace hardware::configstore::V1_0;
@@ -174,6 +181,7 @@
using base::StringAppendF;
using display::PhysicalDisplay;
using display::PhysicalDisplays;
+using frontend::TransactionHandler;
using gui::DisplayInfo;
using gui::GameMode;
using gui::IDisplayEventConnection;
@@ -181,43 +189,18 @@
using gui::LayerMetadata;
using gui::WindowInfo;
using gui::aidl_utils::binderStatusFromStatusT;
-using ui::ColorMode;
using ui::Dataspace;
using ui::DisplayPrimaries;
using ui::RenderIntent;
-using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController;
+using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
namespace hal = android::hardware::graphics::composer::hal;
namespace {
-#pragma clang diagnostic push
-#pragma clang diagnostic error "-Wswitch-enum"
-
-bool isWideColorMode(const ColorMode colorMode) {
- switch (colorMode) {
- case ColorMode::DISPLAY_P3:
- case ColorMode::ADOBE_RGB:
- case ColorMode::DCI_P3:
- case ColorMode::BT2020:
- case ColorMode::DISPLAY_BT2020:
- case ColorMode::BT2100_PQ:
- case ColorMode::BT2100_HLG:
- return true;
- case ColorMode::NATIVE:
- case ColorMode::STANDARD_BT601_625:
- case ColorMode::STANDARD_BT601_625_UNADJUSTED:
- case ColorMode::STANDARD_BT601_525:
- case ColorMode::STANDARD_BT601_525_UNADJUSTED:
- case ColorMode::STANDARD_BT709:
- case ColorMode::SRGB:
- return false;
- }
- return false;
-}
-
-#pragma clang diagnostic pop
+static constexpr int FOUR_K_WIDTH = 3840;
+static constexpr int FOUR_K_HEIGHT = 2160;
// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
@@ -266,6 +249,44 @@
return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
}
+bool isAbove4k30(const ui::DisplayMode& outMode) {
+ using fps_approx_ops::operator>;
+ Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+ return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
+ outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
+}
+
+void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
+ ui::DisplayMode& outMode) {
+ if (isAbove4k30(outMode) &&
+ std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ } else {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ }
+}
+
+HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
+ std::vector<ui::Hdr> hdrTypes;
+ for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ hdrTypes.push_back(type);
+ }
+ }
+ return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
+ displayHdrCapabilities.getDesiredMaxAverageLuminance(),
+ displayHdrCapabilities.getDesiredMinLuminance()};
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -290,7 +311,6 @@
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
-bool SurfaceFlinger::hasWideColorDisplay;
bool SurfaceFlinger::useContextPriority;
Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
@@ -331,15 +351,15 @@
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
mPid(getpid()),
- mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(mFactory.createFrameTracer()),
mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
mCompositionEngine(mFactory.createCompositionEngine()),
mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
mTunnelModeEnabledReporter(sp<TunnelModeEnabledReporter>::make()),
- mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
+ mInternalDisplayDensity(
+ getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()) {
ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
@@ -359,11 +379,11 @@
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
- hasWideColorDisplay = has_wide_color_display(false);
+ mSupportsWideColor = has_wide_color_display(false);
mDefaultCompositionDataspace =
static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace(
- hasWideColorDisplay ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
+ mSupportsWideColor ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
defaultCompositionDataspace = mDefaultCompositionDataspace;
wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
@@ -387,9 +407,6 @@
// debugging stuff...
char value[PROPERTY_VALUE_MAX];
- property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
- mGpuToCpuSupported = !atoi(value);
-
property_get("ro.build.type", value, "user");
mIsUserBuild = strcmp(value, "user") == 0;
@@ -400,7 +417,7 @@
int debugDdms = atoi(value);
ALOGI_IF(debugDdms, "DDMS debugging not supported");
- property_get("debug.sf.enable_gl_backpressure", value, "0");
+ property_get("debug.sf.enable_gl_backpressure", value, "1");
mPropagateBackpressureClientComposition = atoi(value);
ALOGI_IF(mPropagateBackpressureClientComposition,
"Enabling backpressure propagation for Client Composition");
@@ -447,7 +464,9 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlayRenderRate =
+ property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
mTransactionTracing.emplace();
@@ -526,7 +545,6 @@
state.isSecure = secure;
state.displayName = displayName;
mCurrentState.displays.add(token, state);
- mInterceptor->saveDisplayCreation(state);
return token;
}
@@ -544,7 +562,6 @@
ALOGE("%s: Invalid operation on physical display", __func__);
return;
}
- mInterceptor->saveDisplayDeletion(state.sequenceId);
mCurrentState.displays.removeItemsAt(index);
setTransactionFlags(eDisplayTransactionNeeded);
}
@@ -634,7 +651,7 @@
}
renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const {
- return mCompositionEngine->getRenderEngine();
+ return *mRenderEngine;
}
compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const {
@@ -764,6 +781,10 @@
return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
} else if (strcmp(prop, "skiaglthreaded") == 0) {
return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+ } else if (strcmp(prop, "skiavk") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_VK;
+ } else if (strcmp(prop, "skiavkthreaded") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED;
} else {
ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop);
return {};
@@ -775,6 +796,7 @@
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
+ addTransactionReadyFilters();
Mutex::Autolock lock(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
@@ -794,7 +816,8 @@
if (auto type = chooseRenderEngineTypeViaSysProp()) {
builder.setRenderEngineType(type.value());
}
- mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
+ mRenderEngine = renderengine::RenderEngine::create(builder.build());
+ mCompositionEngine->setRenderEngine(mRenderEngine.get());
mMaxRenderTargetSize =
std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
@@ -867,8 +890,6 @@
}
}
- onActiveDisplaySizeChanged(display);
-
// Inform native graphics APIs whether the present timestamp is supported:
const bool presentFenceReliable =
@@ -895,8 +916,8 @@
property_get("persist.sys.sf.native_mode", value, "0");
mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value));
- property_get("persist.sys.sf.color_mode", value, "0");
- mForceColorMode = static_cast<ColorMode>(atoi(value));
+ mForceColorMode =
+ static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
}
void SurfaceFlinger::startBootAnim() {
@@ -954,17 +975,14 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
- ui::StaticDisplayInfo* info) {
- if (!displayToken || !info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
+ if (!info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
@@ -991,26 +1009,10 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
- ui::DynamicDisplayInfo* info) {
- if (!displayToken || !info) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
- if (!displayOpt) {
- return NAME_NOT_FOUND;
- }
-
- const auto& [display, snapshotRef] = *displayOpt;
- const auto& snapshot = snapshotRef.get();
-
+void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
+ const sp<DisplayDevice>& display,
+ const display::DisplaySnapshot& snapshot) {
const auto& displayModes = snapshot.displayModes();
-
info->supportedDisplayModes.clear();
info->supportedDisplayModes.reserve(displayModes.size());
@@ -1054,16 +1056,20 @@
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
-
+ excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
+ outMode);
info->supportedDisplayModes.push_back(outMode);
}
+ info->supportedColorModes = snapshot.filterColorModes(mSupportsWideColor);
+
const PhysicalDisplayId displayId = snapshot.displayId();
- info->activeDisplayModeId = display->refreshRateConfigs().getActiveModePtr()->getId().value();
+ const auto mode = display->refreshRateSelector().getActiveMode();
+ info->activeDisplayModeId = mode.modePtr->getId().value();
+ info->renderFrameRate = mode.fps.getValue();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
- info->supportedColorModes = getDisplayColorModes(displayId);
- info->hdrCapabilities = display->getHdrCapabilities();
+ info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
info->autoLowLatencyModeSupported =
getHwComposer().hasDisplayCapability(displayId,
@@ -1080,7 +1086,47 @@
}
}
}
+}
+status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
+ ui::DynamicDisplayInfo* info) {
+ if (!info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto id_ =
+ DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* info) {
+ if (!displayToken || !info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+ .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+ .and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
return NO_ERROR;
}
@@ -1095,31 +1141,43 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
+void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
ATRACE_CALL();
- if (!info.mode) {
- ALOGW("requested display mode is null");
- return;
- }
- auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId());
+ auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
if (!display) {
ALOGW("%s: display is no longer valid", __func__);
return;
}
- if (display->setDesiredActiveMode(info)) {
- scheduleComposite(FrameHint::kNone);
+ const auto mode = request.mode;
+ const bool emitEvent = request.emitEvent;
- // Start receiving vsync samples now, so that we can detect a period
- // switch.
- mScheduler->resyncToHardwareVsync(true, info.mode->getFps());
- // As we called to set period, we will call to onRefreshRateChangeCompleted once
- // VsyncController model is locked.
- modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+ switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
+ case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
+ scheduleComposite(FrameHint::kNone);
- updatePhaseConfiguration(info.mode->getFps());
- mScheduler->setModeChangePending(true);
+ // Start receiving vsync samples now, so that we can detect a period
+ // switch.
+ mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
+ // As we called to set period, we will call to onRefreshRateChangeCompleted once
+ // VsyncController model is locked.
+ modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+
+ updatePhaseConfiguration(mode.fps);
+ mScheduler->setModeChangePending(true);
+ break;
+ case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
+ mScheduler->setRenderRate(mode.fps);
+ updatePhaseConfiguration(mode.fps);
+ mRefreshRateStats->setRefreshRate(mode.fps);
+ if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+ }
+
+ break;
+ case DisplayDevice::DesiredActiveModeAction::None:
+ break;
}
}
@@ -1132,7 +1190,7 @@
}
const char* const whence = __func__;
- auto future = mScheduler->schedule([=]() -> status_t {
+ auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
const auto displayOpt =
FTL_FAKE_GUARD(mStateLock,
ftl::find_if(mPhysicalDisplays,
@@ -1157,13 +1215,16 @@
}
const Fps fps = *fpsOpt;
+
// Keep the old switching type.
const bool allowGroupSwitching =
- display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
- const scheduler::RefreshRateConfigs::Policy policy{modeId, allowGroupSwitching, {fps, fps}};
- constexpr bool kOverridePolicy = false;
+ display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
- return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+ const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
+ {fps, fps},
+ allowGroupSwitching};
+
+ return setDesiredDisplayModeSpecsInternal(display, policy);
});
return future.get();
@@ -1178,18 +1239,19 @@
}
const auto upcomingModeInfo = display->getUpcomingActiveMode();
- if (!upcomingModeInfo.mode) {
+ if (!upcomingModeInfo.modeOpt) {
// 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().getResolution() != upcomingModeInfo.mode->getResolution()) {
+ if (display->getActiveMode().modePtr->getResolution() !=
+ upcomingModeInfo.modeOpt->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.mode;
+ state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get();
processDisplayChangesLocked();
// processDisplayChangesLocked will update all necessary components so we're done here.
@@ -1200,30 +1262,34 @@
.transform(&PhysicalDisplay::snapshotRef)
.transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(upcomingModeInfo.mode->getId(), snapshot));
+ display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(),
+ upcomingModeInfo.modeOpt->modePtr->getFps(),
+ upcomingModeInfo.modeOpt->fps));
}));
- const Fps refreshRate = upcomingModeInfo.mode->getFps();
+ const Fps refreshRate = upcomingModeInfo.modeOpt->fps;
mRefreshRateStats->setRefreshRate(refreshRate);
updatePhaseConfiguration(refreshRate);
- if (upcomingModeInfo.event != DisplayModeEvent::None) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
+ if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt);
}
}
void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
display->clearDesiredActiveModeState();
- if (isDisplayActiveLocked(display)) {
+ if (display->getPhysicalId() == mActiveDisplayId) {
mScheduler->setModeChangePending(false);
}
}
void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
- const auto refreshRate = display->getDesiredActiveMode()->mode->getFps();
+ const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps();
+ const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps;
clearDesiredActiveModeState(display);
- mScheduler->resyncToHardwareVsync(true, refreshRate);
- updatePhaseConfiguration(refreshRate);
+ mScheduler->resyncToHardwareVsync(true, displayFps);
+ mScheduler->setRenderRate(renderFps);
+ updatePhaseConfiguration(renderFps);
}
void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
@@ -1244,23 +1310,20 @@
// Store the local variable to release the lock.
const auto desiredActiveMode = display->getDesiredActiveMode();
if (!desiredActiveMode) {
- // No desired active mode pending to be applied
+ // No desired active mode pending to be applied.
continue;
}
- if (!isDisplayActiveLocked(display)) {
- // display is no longer the active display, so abort the mode change
+ if (id != mActiveDisplayId) {
+ // Display is no longer the active display, so abort the mode change.
clearDesiredActiveModeState(display);
continue;
}
- const auto desiredModeId = desiredActiveMode->mode->getId();
- const auto refreshRateOpt =
- snapshot.displayModes()
- .get(desiredModeId)
- .transform([](const DisplayModePtr& mode) { return mode->getFps(); });
+ const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
+ const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId);
- if (!refreshRateOpt) {
+ if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
desiredModeId.value());
clearDesiredActiveModeState(display);
@@ -1268,9 +1331,10 @@
}
ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
- to_string(*refreshRateOpt).c_str(), to_string(display->getId()).c_str());
+ to_string(displayModePtrOpt->get()->getFps()).c_str(),
+ to_string(display->getId()).c_str());
- if (display->getActiveMode().getId() == desiredModeId) {
+ if (display->getActiveMode() == desiredActiveMode->modeOpt) {
// we are already in the requested mode, there is nothing left to do
desiredActiveModeChangeDone(display);
continue;
@@ -1279,7 +1343,8 @@
// 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->refreshRateConfigs().isModeAllowed(desiredModeId);
+ const auto displayModeAllowed =
+ display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt);
if (!displayModeAllowed) {
clearDesiredActiveModeState(display);
continue;
@@ -1300,6 +1365,8 @@
ALOGW("initiateModeChange failed: %d", status);
continue;
}
+
+ display->refreshRateSelector().onModeChangeInitiated();
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
if (outTimeline.refreshRequired) {
@@ -1318,8 +1385,7 @@
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
const auto desiredActiveMode = display->getDesiredActiveMode();
- if (desiredActiveMode &&
- display->getActiveMode().getId() == desiredActiveMode->mode->getId()) {
+ if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
desiredActiveModeChangeDone(display);
}
}
@@ -1340,25 +1406,6 @@
future.wait();
}
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
- auto modes = getHwComposer().getColorModes(displayId);
-
- const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-
- // If the display is internal and the configuration claims it's not wide color capable,
- // filter out all wide color modes. The typical reason why this happens is that the
- // hardware is not good enough to support GPU composition of wide color, and thus the
- // OEMs choose to disable this capability.
- if (isInternalDisplay && !hasWideColorDisplay) {
- const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
- modes.erase(newEnd, modes.end());
- }
-
- return modes;
-}
-
status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
ui::DisplayPrimaries& primaries) {
if (!displayToken) {
@@ -1382,31 +1429,32 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode mode) {
if (!displayToken) {
return BAD_VALUE;
}
+ const char* const whence = __func__;
auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
- const auto display = getDisplayDeviceLocked(displayToken);
- if (!display) {
- ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
- decodeColorMode(mode).c_str(), mode, displayToken.get());
+ const auto displayOpt =
+ ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+ .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+ .and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
return NAME_NOT_FOUND;
}
- if (display->isVirtual()) {
- ALOGW("Attempt to set active color mode %s (%d) for virtual display",
- decodeColorMode(mode).c_str(), mode);
- return INVALID_OPERATION;
- }
+ const auto& [display, snapshotRef] = *displayOpt;
+ const auto& snapshot = snapshotRef.get();
- const auto modes = getDisplayColorModes(display->getPhysicalId());
+ const auto modes = snapshot.filterColorModes(mSupportsWideColor);
const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
- if (mode < ColorMode::NATIVE || !exists) {
- ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
- decodeColorMode(mode).c_str(), mode, displayToken.get());
+ if (mode < ui::ColorMode::NATIVE || !exists) {
+ ALOGE("%s: Invalid color mode %s (%d) for display %s", whence,
+ decodeColorMode(mode).c_str(), mode, to_string(snapshot.displayId()).c_str());
return BAD_VALUE;
}
@@ -1429,6 +1477,29 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const {
+ const auto& aidlProperties = getHwComposer().getOverlaySupport();
+ // convert aidl OverlayProperties to gui::OverlayProperties
+ outProperties->combinations.reserve(aidlProperties.combinations.size());
+ for (const auto& combination : aidlProperties.combinations) {
+ std::vector<int32_t> pixelFormats;
+ pixelFormats.reserve(combination.pixelFormats.size());
+ std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(),
+ std::back_inserter(pixelFormats),
+ [](const auto& val) { return static_cast<int32_t>(val); });
+ std::vector<int32_t> dataspaces;
+ dataspaces.reserve(combination.dataspaces.size());
+ std::transform(combination.dataspaces.cbegin(), combination.dataspaces.cend(),
+ std::back_inserter(dataspaces),
+ [](const auto& val) { return static_cast<int32_t>(val); });
+ gui::OverlayProperties::SupportedBufferCombinations outCombination;
+ outCombination.pixelFormats = std::move(pixelFormats);
+ outCombination.dataspaces = std::move(dataspaces);
+ outProperties->combinations.emplace_back(outCombination);
+ }
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
DisplayModeId modeId) {
const char* const whence = __func__;
@@ -1509,7 +1580,8 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
+ bool* success) {
*success = mTimeStats->onPullAtom(atomId, pulledData);
return NO_ERROR;
}
@@ -1584,31 +1656,10 @@
}
*outIsWideColorDisplay =
- display->isPrimary() ? hasWideColorDisplay : display->hasWideColorGamut();
+ display->isPrimary() ? mSupportsWideColor : display->hasWideColorGamut();
return NO_ERROR;
}
-status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
- auto future = mScheduler->schedule([=] {
- Mutex::Autolock lock(mStateLock);
-
- if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
- }
- });
-
- future.wait();
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::injectVSync(nsecs_t when) {
- Mutex::Autolock lock(mStateLock);
- const nsecs_t expectedPresentTime = calculateExpectedPresentTime(TimePoint::fromNs(when)).ns();
- const nsecs_t deadlineTimestamp = expectedPresentTime;
- return mScheduler->injectVSync(when, expectedPresentTime, deadlineTimestamp) ? NO_ERROR
- : BAD_VALUE;
-}
-
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
outLayers->clear();
auto future = mScheduler->schedule([=] {
@@ -1640,8 +1691,12 @@
return BAD_VALUE;
}
- const wp<Layer> stopLayer = fromHandle(stopLayerHandle);
- mRegionSamplingThread->addListener(samplingArea, stopLayer, listener);
+ // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy
+ // the layer here since the caller has a strong ref to the layer's handle.
+ const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle);
+ mRegionSamplingThread->addListener(samplingArea,
+ stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID,
+ listener);
return NO_ERROR;
}
@@ -1854,7 +1909,7 @@
if (hint == FrameHint::kActive) {
mScheduler->resetIdleTimer();
}
- mPowerAdvisor->notifyDisplayUpdateImminent();
+ mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
mScheduler->scheduleFrame();
}
@@ -1898,10 +1953,8 @@
return;
}
- const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
- const bool isActiveDisplay =
- displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
- if (!isActiveDisplay) {
+ if (const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
+ displayId != mActiveDisplayId) {
// For now, we don't do anything with non active display vsyncs.
return;
}
@@ -2097,13 +2150,12 @@
// Save this once per commit + composite to ensure consistency
// TODO (b/240619471): consider removing active display check once AOD is fixed
- const auto activeDisplay =
- FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+ const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
activeDisplay->getPowerMode() == hal::PowerMode::ON;
if (mPowerHintSessionEnabled) {
const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
- const Period vsyncPeriod = Period::fromNs(display->getActiveMode().getVsyncPeriod());
+ const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs());
mPowerAdvisor->setCommitStart(frameTime);
mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
@@ -2228,6 +2280,8 @@
});
}
+ refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
+
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
@@ -2242,10 +2296,13 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
- mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
- layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
+ std::vector<Layer*> layers;
+
+ mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) {
if (auto layerFE = layer->getCompositionEngineLayerFE()) {
+ layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
refreshArgs.layers.push_back(layerFE);
+ layers.push_back(layer);
}
});
refreshArgs.blursAreExpensive = mBlursAreExpensive;
@@ -2275,7 +2332,25 @@
// the scheduler.
const auto presentTime = systemTime();
- mCompositionEngine->present(refreshArgs);
+ {
+ std::vector<LayerSnapshotGuard> layerSnapshotGuards;
+ for (Layer* layer : layers) {
+ layerSnapshotGuards.emplace_back(layer);
+ }
+ mCompositionEngine->present(refreshArgs);
+ }
+
+ for (auto& layer : layers) {
+ CompositionResult compositionResult{
+ layer->getCompositionEngineLayerFE()->stealCompositionResult()};
+ layer->onPreComposition(compositionResult.refreshStartTime);
+ for (auto releaseFence : compositionResult.releaseFences) {
+ layer->onLayerDisplayed(releaseFence);
+ }
+ if (compositionResult.lastClientCompositionFence) {
+ layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
+ }
+ }
mTimeStats->recordFrameDuration(frameTime.ns(), systemTime());
@@ -2735,8 +2810,6 @@
const auto& display = displayOpt->get();
if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
- const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
- mInterceptor->saveDisplayDeletion(state.sequenceId);
mCurrentState.displays.removeItemsAt(index);
}
@@ -2752,6 +2825,8 @@
return nullptr;
}
+ ui::ColorModes colorModes = getHwComposer().getColorModes(displayId);
+
if (displayOpt) {
const auto& display = displayOpt->get();
const auto& snapshot = display.snapshot();
@@ -2766,7 +2841,7 @@
const auto it =
mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
snapshot.connectionType(), std::move(displayModes),
- std::move(deviceProductInfo));
+ std::move(colorModes), std::move(deviceProductInfo));
auto& state = mCurrentState.displays.editValueFor(it->second.token());
state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
@@ -2778,7 +2853,8 @@
mPhysicalDisplays.try_emplace(displayId, token, displayId,
getHwComposer().getDisplayConnectionType(displayId),
- std::move(displayModes), std::move(info.deviceProductInfo));
+ std::move(displayModes), std::move(colorModes),
+ std::move(info.deviceProductInfo));
DisplayDeviceState state;
state.physical = {.id = displayId,
@@ -2788,7 +2864,6 @@
state.displayName = std::move(info.name);
mCurrentState.displays.add(token, state);
- mInterceptor->saveDisplayCreation(state);
return "Connecting";
}
@@ -2816,39 +2891,54 @@
const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
getKernelIdleTimerProperties(compositionDisplay->getId());
- scheduler::RefreshRateConfigs::Config config =
- {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
+ const auto enableFrameRateOverride = [&] {
+ using Config = scheduler::RefreshRateSelector::Config;
+ if (!sysprop::enable_frame_rate_override(false)) {
+ return Config::FrameRateOverride::Disabled;
+ }
+
+ if (sysprop::frame_rate_override_for_native_rates(true)) {
+ return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
+ }
+
+ if (!sysprop::frame_rate_override_global(false)) {
+ return Config::FrameRateOverride::AppOverride;
+ }
+
+ return Config::FrameRateOverride::Enabled;
+ }();
+
+ scheduler::RefreshRateSelector::Config config =
+ {.enableFrameRateOverride = enableFrameRateOverride,
.frameRateMultipleThreshold =
base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
.idleTimerTimeout = idleTimerTimeoutMs,
.kernelIdleTimerController = kernelIdleTimerController};
- creationArgs.refreshRateConfigs =
+ creationArgs.refreshRateSelector =
mPhysicalDisplays.get(physical->id)
.transform(&PhysicalDisplay::snapshotRef)
.transform([&](const display::DisplaySnapshot& snapshot) {
return std::make_shared<
- scheduler::RefreshRateConfigs>(snapshot.displayModes(),
- creationArgs.activeModeId,
- config);
+ scheduler::RefreshRateSelector>(snapshot.displayModes(),
+ creationArgs.activeModeId,
+ config);
})
.value_or(nullptr);
- }
- if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
- creationArgs.isPrimary = id == getPrimaryDisplayIdLocked();
+ creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
if (useColorManagement) {
- std::vector<ColorMode> modes = getHwComposer().getColorModes(*id);
- for (ColorMode colorMode : modes) {
- if (isWideColorMode(colorMode)) {
- creationArgs.hasWideColorGamut = true;
- }
-
- std::vector<RenderIntent> renderIntents =
- getHwComposer().getRenderIntents(*id, colorMode);
- creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
- }
+ mPhysicalDisplays.get(physical->id)
+ .transform(&PhysicalDisplay::snapshotRef)
+ .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+ for (const auto mode : snapshot.colorModes()) {
+ creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
+ creationArgs.hwcColorModes
+ .emplace(mode,
+ getHwComposer().getRenderIntents(physical->id, mode));
+ }
+ }));
}
}
@@ -2880,10 +2970,10 @@
nativeWindowSurface->preallocateBuffers();
- ColorMode defaultColorMode = ColorMode::NATIVE;
+ ui::ColorMode defaultColorMode = ui::ColorMode::NATIVE;
Dataspace defaultDataSpace = Dataspace::UNKNOWN;
if (display->hasWideColorGamut()) {
- defaultColorMode = ColorMode::SRGB;
+ defaultColorMode = ui::ColorMode::SRGB;
defaultDataSpace = Dataspace::V0_SRGB;
}
display->getCompositionDisplay()->setColorProfile(
@@ -2896,7 +2986,9 @@
.transform(&PhysicalDisplay::snapshotRef)
.transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(physical->activeMode->getId(), snapshot));
+ display->setActiveMode(physical->activeMode->getId(),
+ physical->activeMode->getFps(),
+ physical->activeMode->getFps()));
}));
}
@@ -2978,12 +3070,16 @@
displaySurface, producer);
if (mScheduler && !display->isVirtual()) {
- // Display modes are reloaded on hotplug reconnect.
- if (display->isPrimary()) {
- mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
+ const auto displayId = display->getPhysicalId();
+ {
+ // TODO(b/241285876): Annotate `processDisplayAdded` instead.
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ // For hotplug reconnect, renew the registration since display modes have been reloaded.
+ mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector());
}
- dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+ dispatchDisplayHotplugEvent(displayId, true);
}
mDisplays.try_emplace(displayToken, std::move(display));
@@ -2998,6 +3094,7 @@
releaseVirtualDisplay(display->getVirtualId());
} else {
dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
+ mScheduler->unregisterDisplay(display->getPhysicalId());
}
}
@@ -3070,7 +3167,7 @@
(currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
currentState.orientedDisplaySpaceRect);
- if (isDisplayActiveLocked(display)) {
+ if (display->getId() == mActiveDisplayId) {
mActiveDisplayTransformHint = display->getTransformHint();
}
}
@@ -3078,7 +3175,7 @@
currentState.height != drawingState.height) {
display->setDisplaySize(currentState.width, currentState.height);
- if (isDisplayActiveLocked(display)) {
+ if (display->getId() == mActiveDisplayId) {
onActiveDisplaySizeChanged(display);
}
}
@@ -3087,7 +3184,7 @@
void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
mVsyncConfiguration->reset();
- const Fps refreshRate = activeDisplay->getActiveMode().getFps();
+ const Fps refreshRate = activeDisplay->getActiveMode().fps;
updatePhaseConfiguration(refreshRate);
mRefreshRateStats->setRefreshRate(refreshRate);
}
@@ -3138,6 +3235,10 @@
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
if (displayTransactionNeeded) {
processDisplayChangesLocked();
+ mFrontEndDisplayInfos.clear();
+ for (const auto& [_, display] : mDisplays) {
+ mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
+ }
}
mForceTransactionDisplayChange = displayTransactionNeeded;
@@ -3186,20 +3287,21 @@
}
}
- if (!hintDisplay && mDisplays.size() > 0) {
+ if (!hintDisplay) {
// NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
// redraw after transform hint changes. See bug 8508397.
-
// could be null when this layer is using a layerStack
// that is not visible on any display. Also can occur at
// screen off/on times.
- hintDisplay = getDefaultDisplayDeviceLocked();
- }
-
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
+ // U Update: Don't provide stale hints to the clients. For
+ // special cases where we want the app to draw its
+ // 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 {
- ALOGW("Ignoring transform hint update for %s", layer->getDebugName());
+ layer->updateTransformHint(hintDisplay->getTransformHint());
}
});
}
@@ -3228,7 +3330,6 @@
}
doCommitTransactions();
- signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
}
void SurfaceFlinger::updateInputFlinger() {
@@ -3298,7 +3399,7 @@
ALOGE_IF(error != NO_ERROR,
"Error setting display brightness for display %s: %d (%s)",
- display->getDebugName().c_str(), error, strerror(error));
+ to_string(display->getId()).c_str(), error, strerror(error));
}
display->persistBrightness(needsComposite);
}
@@ -3307,29 +3408,6 @@
void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
std::vector<DisplayInfo>& outDisplayInfos) {
- display::DisplayMap<ui::LayerStack, DisplayDevice::InputInfo> displayInputInfos;
-
- for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
- const auto layerStack = display->getLayerStack();
- const auto info = display->getInputInfo();
-
- const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info);
- if (emplaced) {
- continue;
- }
-
- // If the layer stack is mirrored on multiple displays, the first display that is configured
- // to receive input takes precedence.
- auto& otherInfo = it->second;
- if (otherInfo.receivesInput) {
- ALOGW_IF(display->receivesInput(),
- "Multiple displays claim to accept input for the same layer stack: %u",
- layerStack.id);
- } else {
- otherInfo = info;
- }
- }
-
static size_t sNumWindowInfos = 0;
outWindowInfos.reserve(sNumWindowInfos);
sNumWindowInfos = 0;
@@ -3337,8 +3415,8 @@
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- const auto opt = displayInputInfos.get(layer->getLayerStack())
- .transform([](const DisplayDevice::InputInfo& info) {
+ const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack())
+ .transform([](const frontend::DisplayInfo& info) {
return Layer::InputDisplayArgs{&info.transform, info.isSecure};
});
@@ -3347,8 +3425,8 @@
sNumWindowInfos = outWindowInfos.size();
- outDisplayInfos.reserve(displayInputInfos.size());
- for (const auto& [_, info] : displayInputInfos) {
+ outDisplayInfos.reserve(mFrontEndDisplayInfos.size());
+ for (const auto& [_, info] : mFrontEndDisplayInfos) {
outDisplayInfos.push_back(info.info);
}
}
@@ -3361,28 +3439,45 @@
}
}
+ std::vector<LayerSnapshotGuard> layerSnapshotGuards;
+ mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) {
+ if (layer->getLayerSnapshot()->compositionType ==
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
+ layer->updateSnapshot(false /* updateGeometry */);
+ layerSnapshotGuards.emplace_back(layer);
+ }
+ });
+
mCompositionEngine->updateCursorAsync(refreshArgs);
}
-void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) {
+void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
+ if (mBootStage != BootStage::FINISHED) {
+ ALOGV("Currently in the boot stage, skipping display mode changes");
+ return;
+ }
+
+ ATRACE_CALL();
+
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
// Scheduler::chooseRefreshRateForContent
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display || mBootStage != BootStage::FINISHED) {
- return;
- }
- ATRACE_CALL();
+ for (auto& request : modeRequests) {
+ const auto& modePtr = request.mode.modePtr;
+ const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());
- if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
- ALOGV("Skipping disallowed mode %d", mode->getId().value());
- return;
- }
+ if (!display) continue;
- setDesiredActiveMode({std::move(mode), event});
+ if (display->refreshRateSelector().isModeAllowed(request.mode)) {
+ setDesiredActiveMode(std::move(request));
+ } else {
+ ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
+ to_string(display->getId()).c_str());
+ }
+ }
}
void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3397,8 +3492,8 @@
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
LOG_ALWAYS_FATAL_IF(mScheduler);
- const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
- const Fps activeRefreshRate = activeModePtr->getFps();
+ const auto activeMode = display->refreshRateSelector().getActiveMode();
+ const Fps activeRefreshRate = activeMode.fps;
mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
hal::PowerMode::OFF);
@@ -3419,19 +3514,16 @@
!getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
features |= Feature::kPresentFences;
}
+ if (display->refreshRateSelector().kernelIdleTimerController()) {
+ features |= Feature::kKernelIdleTimer;
+ }
mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
static_cast<ISchedulerCallback&>(*this),
features);
- {
- auto configs = display->holdRefreshRateConfigs();
- if (configs->kernelIdleTimerController().has_value()) {
- features |= Feature::kKernelIdleTimer;
- }
+ mScheduler->createVsyncSchedule(features);
+ mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
- mScheduler->createVsyncSchedule(features);
- mScheduler->setRefreshRateConfigs(std::move(configs));
- }
setVsyncEnabled(false);
mScheduler->startTimers();
@@ -3440,15 +3532,11 @@
mAppConnectionHandle =
mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
/*workDuration=*/configs.late.appWorkDuration,
- /*readyDuration=*/configs.late.sfWorkDuration,
- impl::EventThread::InterceptVSyncsCallback());
+ /*readyDuration=*/configs.late.sfWorkDuration);
mSfConnectionHandle =
mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
- /*readyDuration=*/configs.late.sfWorkDuration,
- [this](nsecs_t timestamp) {
- mInterceptor->saveVSyncEvent(timestamp);
- });
+ /*readyDuration=*/configs.late.sfWorkDuration);
mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
@@ -3457,14 +3545,6 @@
sp<RegionSamplingThread>::make(*this,
RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
- // Dispatch a mode change request for the primary display on scheduler
- // initialization, so that the EventThreads always contain a reference to a
- // prior configuration.
- //
- // This is a bit hacky, but this avoids a back-pointer into the main SF
- // classes from EventThread, and there should be no run-time binder cost
- // anyway since there are no connected apps at this point.
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
}
void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3630,9 +3710,9 @@
return !mLayersWithQueuedFrames.empty() && newDataLatched;
}
-status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
+status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parent,
- bool addToRoot, uint32_t* outTransformHint) {
+ uint32_t* outTransformHint) {
if (mNumLayers >= MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
MAX_LAYERS);
@@ -3663,12 +3743,7 @@
{
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- mCreatedLayers.emplace_back(layer, parent, addToRoot);
- }
-
- // attach this layer to the client
- if (client != nullptr) {
- client->attachLayer(handle, layer);
+ mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
}
setTransactionFlags(eTransactionNeeded);
@@ -3692,122 +3767,120 @@
}
}
-int SurfaceFlinger::flushPendingTransactionQueues(
- std::vector<TransactionState>& transactions,
- std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
- bool tryApplyUnsignaled) {
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
- int transactionsPendingBarrier = 0;
- auto it = mPendingTransactionQueues.begin();
- while (it != mPendingTransactionQueues.end()) {
- auto& [applyToken, transactionQueue] = *it;
- while (!transactionQueue.empty()) {
- // if we are in LatchUnsignaledConfig::AutoSingleLayer
- // then we should have only one applyToken for processing.
- // so we can stop further transactions on this applyToken.
- if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer &&
- !applyTokensWithUnsignaledTransactions.empty()) {
- ATRACE_NAME("stopTransactionProcessing");
- break;
- }
-
- auto& transaction = transactionQueue.front();
- const auto ready =
- transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
- transaction.isAutoTimestamp,
- TimePoint::fromNs(transaction.desiredPresentTime),
- transaction.originUid, transaction.states,
- bufferLayersReadyToPresent, transactions.size(),
- tryApplyUnsignaled);
- ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
- if (ready == TransactionReadiness::NotReady) {
- setTransactionFlags(eTransactionFlushNeeded);
- break;
- }
- if (ready == TransactionReadiness::NotReadyBarrier) {
- transactionsPendingBarrier++;
- setTransactionFlags(eTransactionFlushNeeded);
- break;
- }
- transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
- const bool frameNumberChanged = state.bufferData->flags.test(
- BufferData::BufferDataChange::frameNumberChanged);
- if (frameNumberChanged) {
- bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
- } else {
- // Barrier function only used for BBQ which always includes a frame number
- bufferLayersReadyToPresent[state.surface] =
- std::numeric_limits<uint64_t>::max();
- }
- });
- const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
- if (appliedUnsignaled) {
- applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
- }
-
- transactions.emplace_back(std::move(transaction));
- transactionQueue.pop();
- mPendingTransactionCount--;
- ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
- }
-
- if (transactionQueue.empty()) {
- it = mPendingTransactionQueues.erase(it);
- } else {
- it = std::next(it, 1);
- }
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
+ const TransactionHandler::TransactionFlushState& flushState) {
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+ const auto& transaction = *flushState.transaction;
+ ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
+ transaction.frameTimelineInfo.vsyncId);
+ TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+ // Do not present if the desiredPresentTime has not passed unless it is more than
+ // one second in the future. We ignore timestamps more than 1 second in the future
+ // for stability reasons.
+ if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
+ desiredPresentTime < mExpectedPresentTime + 1s) {
+ ATRACE_NAME("not current");
+ return TransactionReadiness::NotReady;
}
- return transactionsPendingBarrier;
+
+ if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
+ ATRACE_NAME("!isVsyncValid");
+ return TransactionReadiness::NotReady;
+ }
+
+ // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
+ // expected present time of this transaction.
+ if (transaction.isAutoTimestamp &&
+ frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+ ATRACE_NAME("frameIsEarly");
+ return TransactionReadiness::NotReady;
+ }
+ return TransactionReadiness::Ready;
+}
+
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+ const TransactionHandler::TransactionFlushState& flushState) {
+ using TransactionReadiness = TransactionHandler::TransactionReadiness;
+ auto ready = TransactionReadiness::Ready;
+ flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool {
+ sp<Layer> layer = LayerHandle::getLayer(s.surface);
+ const auto& transaction = *flushState.transaction;
+ // check for barrier frames
+ if (s.bufferData->hasBarrier &&
+ ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
+ const bool willApplyBarrierFrame =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+ (flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+ s.bufferData->barrierFrameNumber);
+ if (!willApplyBarrierFrame) {
+ ATRACE_NAME("NotReadyBarrier");
+ ready = TransactionReadiness::NotReadyBarrier;
+ return false;
+ }
+ }
+
+ // If backpressure is enabled and we already have a buffer to commit, keep
+ // the transaction in the queue.
+ const bool hasPendingBuffer =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+ if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
+ ready = TransactionReadiness::NotReady;
+ return false;
+ }
+
+ // check fence status
+ const bool allowLatchUnsignaled = shouldLatchUnsignaled(layer, s, transaction.states.size(),
+ flushState.firstTransaction);
+ ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
+ allowLatchUnsignaled ? "true" : "false");
+
+ const bool acquireFenceChanged = s.bufferData &&
+ s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ s.bufferData->acquireFence;
+ const bool fenceSignaled =
+ (!acquireFenceChanged ||
+ s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled);
+ if (!fenceSignaled) {
+ if (!allowLatchUnsignaled) {
+ ready = TransactionReadiness::NotReady;
+ auto& listener = s.bufferData->releaseBufferListener;
+ if (listener &&
+ (flushState.queueProcessTime - transaction.postTime) >
+ std::chrono::nanoseconds(4s).count()) {
+ mTransactionHandler
+ .onTransactionQueueStalled(transaction.id, listener,
+ "Buffer processing hung up due to stuck "
+ "fence. Indicates GPU hang");
+ }
+ return false;
+ }
+
+ ready = enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer
+ ? TransactionReadiness::ReadyUnsignaledSingle
+ : TransactionReadiness::ReadyUnsignaled;
+ }
+ return true;
+ });
+ ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
+ return ready;
+}
+
+void SurfaceFlinger::addTransactionReadyFilters() {
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
+ mTransactionHandler.addTransactionReadyFilter(
+ std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
}
bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock
- std::vector<TransactionState> transactions;
- // Layer handles that have transactions with buffers that are ready to be applied.
- std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
+ std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
{
Mutex::Autolock _l(mStateLock);
- {
- while (!mLocklessTransactionQueue.isEmpty()) {
- auto maybeTransaction = mLocklessTransactionQueue.pop();
- if (!maybeTransaction.has_value()) {
- break;
- }
- auto transaction = maybeTransaction.value();
- mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
- }
-
- // Transactions with a buffer pending on a barrier may be on a different applyToken
- // than the transaction which satisfies our barrier. In fact this is the exact use case
- // that the primitive is designed for. This means we may first process
- // the barrier dependent transaction, determine it ineligible to complete
- // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
- // The barrier dependent transaction was eligible to be presented in this frame
- // but we would have prevented it without case. To fix this we continually
- // loop through flushPendingTransactionQueues until we perform an iteration
- // where the number of transactionsPendingBarrier doesn't change. This way
- // we can continue to resolve dependency chains of barriers as far as possible.
- int lastTransactionsPendingBarrier = 0;
- int transactionsPendingBarrier = 0;
- do {
- lastTransactionsPendingBarrier = transactionsPendingBarrier;
- transactionsPendingBarrier =
- flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
- /*tryApplyUnsignaled*/ false);
- } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
-
- // We collected all transactions that could apply without latching unsignaled buffers.
- // If we are allowing latch unsignaled of some form, now it's the time to go over the
- // transactions that were not applied and try to apply them unsignaled.
- if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
- flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
- /*tryApplyUnsignaled*/ true);
- }
-
- return applyTransactions(transactions, vsyncId);
- }
+ return applyTransactions(transactions, vsyncId);
}
}
@@ -3825,10 +3898,6 @@
transaction.permissions, transaction.hasListenerCallbacks,
transaction.listenerCallbacks, transaction.originPid,
transaction.originUid, transaction.id);
- if (transaction.transactionCommittedSignal) {
- mTransactionCommittedSignals.emplace_back(
- std::move(transaction.transactionCommittedSignal));
- }
}
if (mTransactionTracing) {
@@ -3838,7 +3907,7 @@
}
bool SurfaceFlinger::transactionFlushNeeded() {
- return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+ return mTransactionHandler.hasPendingTransactions();
}
bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
@@ -3864,7 +3933,7 @@
}
bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
- size_t numStates, size_t totalTXapplied) const {
+ size_t numStates, bool firstTransaction) const {
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
return false;
@@ -3883,9 +3952,9 @@
}
if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
- if (totalTXapplied > 0) {
- ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
- __func__, totalTXapplied);
+ if (!firstTransaction) {
+ ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
+ __func__);
return false;
}
@@ -3909,146 +3978,8 @@
return true;
}
-auto SurfaceFlinger::transactionIsReadyToBeApplied(
- TransactionState& transaction, const FrameTimelineInfo& info, bool isAutoTimestamp,
- TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>& states,
- const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
- bufferLayersReadyToPresent,
- size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
- ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
- // Do not present if the desiredPresentTime has not passed unless it is more than one second
- // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
- if (!isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
- desiredPresentTime < mExpectedPresentTime + 1s) {
- ATRACE_NAME("not current");
- return TransactionReadiness::NotReady;
- }
-
- if (!mScheduler->isVsyncValid(mExpectedPresentTime, originUid)) {
- ATRACE_NAME("!isVsyncValid");
- return TransactionReadiness::NotReady;
- }
-
- // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
- // present time of this transaction.
- if (isAutoTimestamp && frameIsEarly(mExpectedPresentTime, VsyncId{info.vsyncId})) {
- ATRACE_NAME("frameIsEarly");
- return TransactionReadiness::NotReady;
- }
-
- bool fenceUnsignaled = false;
- auto queueProcessTime = systemTime();
- for (const ComposerState& state : states) {
- const layer_state_t& s = state.state;
-
- sp<Layer> layer = nullptr;
- if (s.surface) {
- layer = fromHandle(s.surface).promote();
- } else if (s.hasBufferChanges()) {
- ALOGW("Transaction with buffer, but no Layer?");
- continue;
- }
- if (!layer) {
- continue;
- }
-
- if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
- ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
- const bool willApplyBarrierFrame =
- (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
- (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
- if (!willApplyBarrierFrame) {
- ATRACE_NAME("NotReadyBarrier");
- return TransactionReadiness::NotReadyBarrier;
- }
- }
-
- const bool allowLatchUnsignaled = tryApplyUnsignaled &&
- shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
- ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
- allowLatchUnsignaled ? "true" : "false");
-
- const bool acquireFenceChanged = s.bufferData &&
- s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
- s.bufferData->acquireFence;
- fenceUnsignaled = fenceUnsignaled ||
- (acquireFenceChanged &&
- s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);
-
- if (fenceUnsignaled && !allowLatchUnsignaled) {
- if (!transaction.sentFenceTimeoutWarning &&
- queueProcessTime - transaction.postTime > std::chrono::nanoseconds(4s).count()) {
- transaction.sentFenceTimeoutWarning = true;
- auto listener = s.bufferData->releaseBufferListener;
- if (listener) {
- listener->onTransactionQueueStalled();
- }
- }
-
- ATRACE_NAME("fence unsignaled");
- return TransactionReadiness::NotReady;
- }
-
- if (s.hasBufferChanges()) {
- // If backpressure is enabled and we already have a buffer to commit, keep the
- // transaction in the queue.
- const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
- bufferLayersReadyToPresent.end();
- if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
- ATRACE_NAME("hasPendingBuffer");
- return TransactionReadiness::NotReady;
- }
- }
- }
- return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
-}
-
-void SurfaceFlinger::queueTransaction(TransactionState& state) {
- // Generate a CountDownLatch pending state if this is a synchronous transaction.
- if (state.flags & eSynchronous) {
- state.transactionCommittedSignal =
- std::make_shared<CountDownLatch>(CountDownLatch::eSyncTransaction);
- }
-
- mLocklessTransactionQueue.push(state);
- mPendingTransactionCount++;
- ATRACE_INT("TransactionQueue", mPendingTransactionCount.load());
-
- const auto schedule = [](uint32_t flags) {
- if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
- if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
- return TransactionSchedule::Late;
- }(state.flags);
-
- const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
-
- setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
-}
-
-void SurfaceFlinger::waitForSynchronousTransaction(
- const CountDownLatch& transactionCommittedSignal) {
- // applyTransactionState is called on the main SF thread. While a given process may wish
- // to wait on synchronous transactions, the main SF thread should apply the transaction and
- // set the value to notify this after committed.
- if (!transactionCommittedSignal.wait_until(
- std::chrono::nanoseconds(mAnimationTransactionTimeout))) {
- ALOGE("setTransactionState timed out!");
- }
-}
-
-void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
- for (auto it = mTransactionCommittedSignals.begin();
- it != mTransactionCommittedSignals.end();) {
- if ((*it)->countDown(flag)) {
- it = mTransactionCommittedSignals.erase(it);
- } else {
- it++;
- }
- }
-}
-
status_t SurfaceFlinger::setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
@@ -4080,7 +4011,25 @@
IPCThreadState* ipc = IPCThreadState::self();
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
- TransactionState state{frameTimelineInfo, states,
+
+ std::vector<ResolvedComposerState> resolvedStates;
+ resolvedStates.reserve(states.size());
+ for (auto& state : states) {
+ resolvedStates.emplace_back(std::move(state));
+ auto& resolvedState = resolvedStates.back();
+ if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
+ resolvedState.state.surface) {
+ sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
+ std::string layerName = (layer) ?
+ layer->getDebugName() : std::to_string(resolvedState.state.layerId);
+ resolvedState.externalTexture =
+ getExternalTextureFromBufferData(*resolvedState.state.bufferData,
+ layerName.c_str(), transactionId);
+ mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
+ }
+ }
+
+ TransactionState state{frameTimelineInfo, resolvedStates,
displays, flags,
applyToken, inputWindowCommands,
desiredPresentTime, isAutoTimestamp,
@@ -4089,27 +4038,26 @@
listenerCallbacks, originPid,
originUid, transactionId};
- // Check for incoming buffer updates and increment the pending buffer count.
- state.traverseStatesWithBuffers([&](const layer_state_t& state) {
- mBufferCountTracker.increment(state.surface->localBinder());
- });
-
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
}
- queueTransaction(state);
- // Check the pending state to make sure the transaction is synchronous.
- if (state.transactionCommittedSignal) {
- waitForSynchronousTransaction(*state.transactionCommittedSignal);
- }
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
+ }(state.flags);
+
+ const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
+ setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
+ mTransactionHandler.queueTransaction(std::move(state));
return NO_ERROR;
}
bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
+ std::vector<ResolvedComposerState>& states,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer,
@@ -4118,7 +4066,8 @@
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
- for (const DisplayState& display : displays) {
+ for (DisplayState& display : displays) {
+ display.sanitize(permissions);
transactionFlags |= setDisplayStateLocked(display);
}
@@ -4130,12 +4079,12 @@
}
uint32_t clientStateFlags = 0;
- for (int i = 0; i < states.size(); i++) {
- ComposerState& state = states.editItemAt(i);
- clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
- isAutoTimestamp, postTime, permissions);
- if ((flags & eAnimation) && state.state.surface) {
- if (const auto layer = fromHandle(state.state.surface).promote()) {
+ for (auto& resolvedState : states) {
+ clientStateFlags |=
+ setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
+ isAutoTimestamp, postTime, permissions, transactionId);
+ if ((flags & eAnimation) && resolvedState.state.surface) {
+ if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
mScheduler->recordLayerHistory(layer.get(),
isAutoTimestamp ? 0 : desiredPresentTime,
@@ -4153,25 +4102,22 @@
}
if (uncacheBuffer.isValid()) {
- ClientCache::getInstance().erase(uncacheBuffer);
+ sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+ if (buffer != nullptr) {
+ mBufferIdsToUncache.push_back(buffer->getId());
+ }
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
// anyway. This can be used as a flush mechanism for previous async transactions.
// Empty animation transaction can be used to simulate back-pressure, so also force a
// transaction for empty animation transactions.
- if (transactionFlags == 0 &&
- ((flags & eSynchronous) || (flags & eAnimation))) {
+ if (transactionFlags == 0 && (flags & eAnimation)) {
transactionFlags = eTransactionNeeded;
}
bool needsTraversal = false;
if (transactionFlags) {
- if (mInterceptor->isEnabled()) {
- mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
- originPid, originUid, transactionId);
- }
-
// We are on the main thread, we are about to preform a traversal. Clear the traversal bit
// so we don't have to wake up again next frame to preform an unnecessary traversal.
if (transactionFlags & eTraversalNeeded) {
@@ -4253,9 +4199,10 @@
}
uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
- ComposerState& composerState,
+ ResolvedComposerState& composerState,
int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint32_t permissions) {
+ int64_t postTime, uint32_t permissions,
+ uint64_t transactionId) {
layer_state_t& s = composerState.state;
s.sanitize(permissions);
@@ -4281,7 +4228,7 @@
uint32_t flags = 0;
sp<Layer> layer = nullptr;
if (s.surface) {
- layer = fromHandle(s.surface).promote();
+ layer = LayerHandle::getLayer(s.surface);
} else {
// The client may provide us a null handle. Treat it as if the layer was removed.
ALOGW("Attempt to set client state with a null layer handle");
@@ -4294,6 +4241,8 @@
return 0;
}
+ ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current);
+
// Only set by BLAST adapter layers
if (what & layer_state_t::eProducerDisconnect) {
layer->onDisconnect();
@@ -4344,12 +4293,10 @@
}
}
if (what & layer_state_t::eAlphaChanged) {
- if (layer->setAlpha(s.alpha))
- flags |= eTraversalNeeded;
+ if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorChanged) {
- if (layer->setColor(s.color))
- flags |= eTraversalNeeded;
+ if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorTransformChanged) {
if (layer->setColorTransform(s.colorTransform)) {
@@ -4357,7 +4304,7 @@
}
}
if (what & layer_state_t::eBackgroundColorChanged) {
- if (layer->setBackgroundColor(s.color, s.bgColorAlpha, s.bgColorDataspace)) {
+ if (layer->setBackgroundColor(s.color.rgb, s.bgColorAlpha, s.bgColorDataspace)) {
flags |= eTraversalNeeded;
}
}
@@ -4406,8 +4353,8 @@
flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
}
}
- if (what & layer_state_t::eTransformChanged) {
- if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
+ if (what & layer_state_t::eBufferTransformChanged) {
+ if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eTransformToDisplayInverseChanged) {
if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
@@ -4547,10 +4494,9 @@
}
if (what & layer_state_t::eBufferChanged) {
- std::shared_ptr<renderengine::ExternalTexture> buffer =
- getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName());
- if (layer->setBuffer(buffer, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
- dequeueBufferTimestamp, frameTimelineInfo)) {
+ if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
+ desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
+ frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -4560,6 +4506,13 @@
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
+
+ // if the layer has been parented on to a new display, update its transform hint.
+ if (((flags & eTransformHintUpdateNeeded) == 0) &&
+ oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) {
+ flags |= eTransformHintUpdateNeeded;
+ }
+
return flags;
}
@@ -4569,23 +4522,25 @@
}
status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args,
- const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
- int32_t* outLayerId) {
+ const sp<IBinder>& mirrorFromHandle,
+ gui::CreateSurfaceResult& outResult) {
if (!mirrorFromHandle) {
return NAME_NOT_FOUND;
}
sp<Layer> mirrorLayer;
sp<Layer> mirrorFrom;
+ LayerCreationArgs mirrorArgs(args);
{
Mutex::Autolock _l(mStateLock);
- mirrorFrom = fromHandle(mirrorFromHandle).promote();
+ mirrorFrom = LayerHandle::getLayer(mirrorFromHandle);
if (!mirrorFrom) {
return NAME_NOT_FOUND;
}
- LayerCreationArgs mirrorArgs = args;
mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
- status_t result = createEffectLayer(mirrorArgs, outHandle, &mirrorLayer);
+ mirrorArgs.mirrorLayerHandle = mirrorFromHandle;
+ mirrorArgs.addToRoot = false;
+ status_t result = createEffectLayer(mirrorArgs, &outResult.handle, &mirrorLayer);
if (result != NO_ERROR) {
return result;
}
@@ -4593,17 +4548,19 @@
mirrorLayer->setClonedChild(mirrorFrom->createClone());
}
- *outLayerId = mirrorLayer->sequence;
+ outResult.layerId = mirrorLayer->sequence;
+ outResult.layerName = String16(mirrorLayer->getDebugName());
if (mTransactionTracing) {
- mTransactionTracing->onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
- args.name, mirrorFrom->sequence);
+ mTransactionTracing->onMirrorLayerAdded(outResult.handle->localBinder(),
+ mirrorLayer->sequence, args.name,
+ mirrorFrom->sequence);
}
- return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
- false /* addToRoot */, nullptr /* outTransformHint */);
+ return addClientLayer(mirrorArgs, outResult.handle, mirrorLayer /* layer */,
+ nullptr /* parent */, nullptr /* outTransformHint */);
}
status_t SurfaceFlinger::mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
- sp<IBinder>* outHandle, int32_t* outLayerId) {
+ gui::CreateSurfaceResult& outResult) {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != AID_SHELL) {
@@ -4624,13 +4581,14 @@
}
layerStack = display->getLayerStack();
- LayerCreationArgs mirrorArgs = args;
+ LayerCreationArgs mirrorArgs(args);
mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
- result = createEffectLayer(mirrorArgs, outHandle, &rootMirrorLayer);
- *outLayerId = rootMirrorLayer->sequence;
- result |= addClientLayer(args.client, *outHandle, rootMirrorLayer /* layer */,
- nullptr /* parent */, true /* addToRoot */,
- nullptr /* outTransformHint */);
+ mirrorArgs.addToRoot = true;
+ result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
+ outResult.layerId = rootMirrorLayer->sequence;
+ outResult.layerName = String16(rootMirrorLayer->getDebugName());
+ result |= addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
+ nullptr /* parent */, nullptr /* outTransformHint */);
}
if (result != NO_ERROR) {
@@ -4638,26 +4596,20 @@
}
if (mTransactionTracing) {
- mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), *outLayerId, args.name,
- args.flags, -1 /* parentId */);
+ mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), outResult.layerId,
+ args.name, args.flags, -1 /* parentId */);
}
{
std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
- mMirrorDisplays.emplace_back(layerStack, *outHandle, args.client);
+ mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
}
setTransactionFlags(eTransactionFlushNeeded);
return NO_ERROR;
}
-status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
- const sp<IBinder>& parentHandle, int32_t* outLayerId,
- const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
- ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
- "Expected only one of parentLayer or parentHandle to be non-null. "
- "Programmer error?");
-
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) {
status_t result = NO_ERROR;
sp<Layer> layer;
@@ -4669,11 +4621,11 @@
args.flags |= ISurfaceComposerClient::eNoColorFill;
FMT_FALLTHROUGH;
case ISurfaceComposerClient::eFXSurfaceEffect: {
- result = createBufferStateLayer(args, outHandle, &layer);
+ result = createBufferStateLayer(args, &outResult.handle, &layer);
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
if (pendingBufferCounter) {
std::string counterName = layer->getPendingBufferCounterName();
- mBufferCountTracker.add((*outHandle)->localBinder(), counterName,
+ mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
pendingBufferCounter);
}
} break;
@@ -4686,34 +4638,30 @@
return result;
}
- bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
- wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
- if (parentHandle != nullptr && parent == nullptr) {
- ALOGE("Invalid parent handle %p.", parentHandle.get());
- addToRoot = false;
- }
- if (parentLayer != nullptr) {
- addToRoot = false;
+ args.addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
+ // We can safely promote the parent layer in binder thread because we have a strong reference
+ // to the layer's handle inside this scope.
+ sp<Layer> parent = LayerHandle::getLayer(args.parentHandle.promote());
+ if (args.parentHandle != nullptr && parent == nullptr) {
+ ALOGE("Invalid parent handle %p", args.parentHandle.promote().get());
+ args.addToRoot = false;
}
- int parentId = -1;
- // We can safely promote the layer in binder thread because we have a strong reference
- // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
- sp<Layer> parentSp = parent.promote();
- if (parentSp != nullptr) {
- parentId = parentSp->getSequence();
- }
+ const int parentId = parent ? parent->getSequence() : -1;
if (mTransactionTracing) {
- mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
- args.flags, parentId);
+ mTransactionTracing->onLayerAdded(outResult.handle->localBinder(), layer->sequence,
+ args.name, args.flags, parentId);
}
- result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
+ uint32_t outTransformHint;
+ result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
if (result != NO_ERROR) {
return result;
}
- *outLayerId = layer->sequence;
+ outResult.transformHint = static_cast<int32_t>(outTransformHint);
+ outResult.layerId = layer->sequence;
+ outResult.layerName = String16(layer->getDebugName());
return result;
}
@@ -4738,7 +4686,7 @@
setTransactionFlags(eTransactionNeeded);
}
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t /* layerId */) {
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
@@ -4756,7 +4704,7 @@
LOG_ALWAYS_FATAL_IF(token == nullptr);
// reset screen orientation and use primary layer stack
- Vector<ComposerState> state;
+ std::vector<ResolvedComposerState> state;
Vector<DisplayState> displays;
DisplayState d;
d.what = DisplayState::eDisplayProjectionChanged |
@@ -4779,8 +4727,6 @@
{}, mPid, getuid(), transactionId);
setPowerModeInternal(display, hal::PowerMode::ON);
-
- mActiveDisplayTransformHint = display->getTransformHint();
}
void SurfaceFlinger::initializeDisplays() {
@@ -4799,31 +4745,29 @@
const auto displayId = display->getPhysicalId();
ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
- std::optional<hal::PowerMode> currentMode = display->getPowerMode();
- if (currentMode.has_value() && mode == *currentMode) {
+ const auto currentModeOpt = display->getPowerMode();
+ if (currentModeOpt == mode) {
return;
}
+ const bool isActiveDisplay = displayId == mActiveDisplayId;
const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
.transform(&PhysicalDisplay::isInternal)
.value_or(false);
- const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
- if (isInternalDisplay && activeDisplay != display && activeDisplay &&
- activeDisplay->isPoweredOn()) {
- ALOGW("Trying to change power mode on non active display while the active display is ON");
- }
+ const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
+ const bool isActiveDisplayPoweredOn = activeDisplay && activeDisplay->isPoweredOn();
+
+ ALOGW_IF(display != activeDisplay && isInternalDisplay && isActiveDisplayPoweredOn,
+ "Trying to change power mode on inactive display without powering off active display");
display->setPowerMode(mode);
- if (mInterceptor->isEnabled()) {
- mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
- }
- const auto refreshRate = display->refreshRateConfigs().getActiveMode().getFps();
- if (*currentMode == hal::PowerMode::OFF) {
+ const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
+ if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
// Turn on the display
- if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
- onActiveDisplayChangedLocked(display);
+ if (isInternalDisplay && !isActiveDisplayPoweredOn) {
+ onActiveDisplayChangedLocked(activeDisplay, display);
}
// Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
// We can merge the syscall later.
@@ -4834,7 +4778,7 @@
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
getHwComposer().setPowerMode(displayId, mode);
- if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
+ if (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) {
setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, refreshRate);
@@ -4850,7 +4794,7 @@
if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
}
- if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
+ if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
@@ -4864,7 +4808,7 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
+ if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
mVisibleRegionsDirty = true;
scheduleRepaint();
@@ -4873,7 +4817,7 @@
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
- if (isDisplayActiveLocked(display)) {
+ if (isActiveDisplay) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
@@ -4883,7 +4827,7 @@
getHwComposer().setPowerMode(displayId, mode);
}
- if (isDisplayActiveLocked(display)) {
+ if (isActiveDisplay) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
mScheduler->setDisplayPowerMode(mode);
@@ -4925,17 +4869,18 @@
{"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
{"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
- {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
+ {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+ {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
{"--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)},
+ {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
{"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
- {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
- {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
};
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4998,13 +4943,6 @@
}
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
- if (asProto) {
- mLayerTracing.writeToFile();
- if (mTransactionTracing) {
- mTransactionTracing->writeToFile();
- }
- }
-
return doDump(fd, DumpArgs(), asProto);
}
@@ -5066,24 +5004,39 @@
result.append("]");
}
-void SurfaceFlinger::dumpVSync(std::string& result) const {
- mScheduler->dump(result);
+void SurfaceFlinger::dumpScheduler(std::string& result) const {
+ utils::Dumper dumper{result};
+
+ mScheduler->dump(dumper);
+
+ // TODO(b/241286146): Move to Scheduler.
+ {
+ utils::Dumper::Indent indent(dumper);
+ dumper.dump("lastHwcVsyncState"sv, mLastHWCVsyncState);
+ dumper.dump("pendingHwcVsyncState"sv, mHWCVsyncPendingState);
+ }
+ dumper.eol();
+
+ // TODO(b/241285876): Move to DisplayModeController.
+ dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
+ dumper.eol();
mRefreshRateStats->dump(result);
- result.append("\n");
+ dumper.eol();
mVsyncConfiguration->dump(result);
StringAppendF(&result,
- " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
+ " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64
+ " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
+}
- StringAppendF(&result, "(mode override by backdoor: %s)\n\n",
- mDebugDisplayModeSetByBackdoor ? "yes" : "no");
-
+void SurfaceFlinger::dumpEvents(std::string& result) const {
mScheduler->dump(mAppConnectionHandle, result);
+}
+
+void SurfaceFlinger::dumpVsync(std::string& result) const {
mScheduler->dumpVsync(result);
- StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n",
- to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str());
}
void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
@@ -5101,18 +5054,24 @@
}
void SurfaceFlinger::dumpDisplays(std::string& result) const {
+ utils::Dumper dumper{result};
+
for (const auto& [id, display] : mPhysicalDisplays) {
+ utils::Dumper::Section section(dumper, ftl::Concat("Display ", id.value).str());
+
+ display.snapshot().dump(dumper);
+
if (const auto device = getDisplayDeviceLocked(id)) {
- device->dump(result);
+ device->dump(dumper);
}
- display.snapshot().dump(result);
- result += '\n';
}
for (const auto& [token, display] : mDisplays) {
if (display->isVirtual()) {
- display->dump(result);
- result += '\n';
+ const auto displayId = display->getId();
+ utils::Dumper::Section section(dumper,
+ ftl::Concat("Virtual Display ", displayId.value).str());
+ display->dump(dumper);
}
}
}
@@ -5168,35 +5127,45 @@
}
void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
- StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay);
+ StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor);
StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
StringAppendF(&result, "DisplayColorSetting: %s\n",
decodeDisplayColorSetting(mDisplayColorSetting).c_str());
// TODO: print out if wide-color mode is active or not
- for (const auto& [token, display] : mDisplays) {
- const auto displayId = PhysicalDisplayId::tryCast(display->getId());
- if (!displayId) {
- continue;
- }
-
- StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str());
- std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
- for (auto&& mode : modes) {
+ for (const auto& [id, display] : mPhysicalDisplays) {
+ StringAppendF(&result, "Display %s color modes:\n", to_string(id).c_str());
+ for (const auto mode : display.snapshot().colorModes()) {
StringAppendF(&result, " %s (%d)\n", decodeColorMode(mode).c_str(), mode);
}
- ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
- StringAppendF(&result, " Current color mode: %s (%d)\n",
- decodeColorMode(currentMode).c_str(), currentMode);
+ if (const auto display = getDisplayDeviceLocked(id)) {
+ ui::ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
+ StringAppendF(&result, " Current color mode: %s (%d)\n",
+ decodeColorMode(currentMode).c_str(), currentMode);
+ }
}
result.append("\n");
}
LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
+ std::unordered_set<uint64_t> stackIdsToSkip;
+
+ // Determine if virtual layers display should be skipped
+ if ((traceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
+ for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
+ if (display->isVirtual()) {
+ stackIdsToSkip.insert(display->getLayerStack().id);
+ }
+ }
+ }
+
LayersProto layersProto;
for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+ if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
+ continue;
+ }
layer->writeToProto(layersProto, traceFlags);
}
@@ -5252,7 +5221,7 @@
std::string result;
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+ [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
}
return result;
});
@@ -5269,11 +5238,11 @@
}
StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
- (isDisplayActiveLocked(display) ? "active" : "inactive"));
+ displayId == mActiveDisplayId ? "active" : "inactive");
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
- mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+ mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
result.append("\n");
}
}
@@ -5313,7 +5282,9 @@
colorizer.bold(result);
result.append("Scheduler:\n");
colorizer.reset(result);
- dumpVSync(result);
+ dumpScheduler(result);
+ dumpEvents(result);
+ dumpVsync(result);
result.append("\n");
StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
@@ -5358,14 +5329,12 @@
StringAppendF(&result, " orientation=%s, isPoweredOn=%d\n",
toCString(display->getOrientation()), display->isPoweredOn());
}
- StringAppendF(&result,
- " transaction-flags : %08x\n"
- " gpu_to_cpu_unsupported : %d\n",
- mTransactionFlags.load(), !mGpuToCpuSupported);
+ StringAppendF(&result, " transaction-flags : %08x\n", mTransactionFlags.load());
if (const auto display = getDefaultDisplayDeviceLocked()) {
std::string fps, xDpi, yDpi;
- if (const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr()) {
+ if (const auto activeModePtr =
+ display->refreshRateSelector().getActiveMode().modePtr.get()) {
fps = to_string(activeModePtr->getFps());
const auto dpi = activeModePtr->getDpi();
@@ -5708,17 +5677,8 @@
mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
return NO_ERROR;
}
- case 1020: { // Layer updates interceptor
- n = data.readInt32();
- if (n) {
- ALOGV("Interceptor enabled");
- mInterceptor->enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
- }
- else{
- ALOGV("Interceptor disabled");
- mInterceptor->disable();
- }
- return NO_ERROR;
+ case 1020: { // Unused
+ return NAME_NOT_FOUND;
}
case 1021: { // Disable HWC virtual displays
const bool enable = data.readInt32() != 0;
@@ -5733,12 +5693,11 @@
updateColorMatrixLocked();
return NO_ERROR;
}
- case 1023: { // Set native mode
- int32_t colorMode;
-
+ case 1023: { // Set color mode.
mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
- if (data.readInt32(&colorMode) == NO_ERROR) {
- mForceColorMode = static_cast<ColorMode>(colorMode);
+
+ if (int32_t colorMode; data.readInt32(&colorMode) == NO_ERROR) {
+ mForceColorMode = static_cast<ui::ColorMode>(colorMode);
}
scheduleRepaint();
return NO_ERROR;
@@ -5911,7 +5870,7 @@
case 1036: {
if (data.readInt32() > 0) { // turn on
return mScheduler
- ->schedule([this] {
+ ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
const auto display =
FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
@@ -5921,24 +5880,22 @@
// defaultMode. The defaultMode doesn't matter for the override
// policy though, since we set allowGroupSwitching to true, so it's
// not a problem.
- scheduler::RefreshRateConfigs::Policy overridePolicy;
- overridePolicy.defaultMode = display->refreshRateConfigs()
+ scheduler::RefreshRateSelector::OverridePolicy overridePolicy;
+ overridePolicy.defaultMode = display->refreshRateSelector()
.getDisplayManagerPolicy()
.defaultMode;
overridePolicy.allowGroupSwitching = true;
- constexpr bool kOverridePolicy = true;
- return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
- kOverridePolicy);
+ return setDesiredDisplayModeSpecsInternal(display, overridePolicy);
})
.get();
} else { // turn off
return mScheduler
- ->schedule([this] {
+ ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
const auto display =
FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
- constexpr bool kOverridePolicy = true;
- return setDesiredDisplayModeSpecsInternal(display, {},
- kOverridePolicy);
+ return setDesiredDisplayModeSpecsInternal(
+ display,
+ scheduler::RefreshRateSelector::NoOverridePolicy{});
})
.get();
}
@@ -6049,7 +6006,7 @@
if (!updateOverlay) return;
// Update the overlay on the main thread to avoid race conditions with
- // mRefreshRateConfigs->getActiveMode()
+ // RefreshRateSelector::getActiveMode
static_cast<void>(mScheduler->schedule([=] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
@@ -6060,7 +6017,8 @@
const auto desiredActiveMode = display->getDesiredActiveMode();
const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
- ? std::make_optional(desiredActiveMode->mode->getId())
+ ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId())
+
: std::nullopt;
const bool timerExpired = mKernelIdleTimerEnabled && expired;
@@ -6113,7 +6071,7 @@
}
void SurfaceFlinger::toggleKernelIdleTimer() {
- using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+ using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
const auto display = getDefaultDisplayDeviceLocked();
if (!display) {
@@ -6124,12 +6082,12 @@
// If the support for kernel idle timer is disabled for the active display,
// don't do anything.
const std::optional<KernelIdleTimerController> kernelIdleTimerController =
- display->refreshRateConfigs().kernelIdleTimerController();
+ display->refreshRateSelector().kernelIdleTimerController();
if (!kernelIdleTimerController.has_value()) {
return;
}
- const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction();
+ const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction();
switch (action) {
case KernelIdleTimerAction::TurnOff:
@@ -6145,7 +6103,7 @@
if (!mKernelIdleTimerEnabled) {
ATRACE_INT("KernelIdleTimer", 1);
const std::chrono::milliseconds timeout =
- display->refreshRateConfigs().getIdleTimerTimeout();
+ display->refreshRateSelector().getIdleTimerTimeout();
updateKernelIdleTimer(timeout, kernelIdleTimerController.value(),
display->getPhysicalId());
mKernelIdleTimerEnabled = true;
@@ -6167,18 +6125,6 @@
const int mApi;
};
-static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
- switch (colorMode) {
- case ColorMode::DISPLAY_P3:
- case ColorMode::BT2100_PQ:
- case ColorMode::BT2100_HLG:
- case ColorMode::DISPLAY_BT2020:
- return Dataspace::DISPLAY_P3;
- default:
- return Dataspace::V0_SRGB;
- }
-}
-
static bool hasCaptureBlackoutContentPermission() {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -6290,15 +6236,11 @@
reqSize = display->getLayerStackSpaceRect().getSize();
}
- // The dataspace is depended on the color mode of display, that could use non-native mode
- // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
- // and failed if display is not in native mode. This provide a way to force using native
- // colors when capture.
- dataspace = args.dataspace;
- if (dataspace == ui::Dataspace::UNKNOWN) {
- const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
- dataspace = pickDataspaceFromColorMode(colorMode);
- }
+ // Allow the caller to specify a dataspace regardless of the display's color mode, e.g. if
+ // it wants sRGB regardless of the display's wide color mode.
+ dataspace = args.dataspace == ui::Dataspace::UNKNOWN
+ ? ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode)
+ : args.dataspace;
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
@@ -6333,9 +6275,7 @@
displayWeak = display;
layerStack = display->getLayerStack();
size = display->getLayerStackSpaceRect().getSize();
-
- dataspace =
- pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
+ dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
@@ -6383,7 +6323,7 @@
{
Mutex::Autolock lock(mStateLock);
- parent = fromHandle(args.layerHandle).promote();
+ parent = LayerHandle::getLayer(args.layerHandle);
if (parent == nullptr) {
ALOGE("captureLayers called with an invalid or removed parent");
return NAME_NOT_FOUND;
@@ -6414,7 +6354,7 @@
reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
for (const auto& handle : args.excludeHandles) {
- sp<Layer> excludeLayer = fromHandle(handle).promote();
+ sp<Layer> excludeLayer = LayerHandle::getLayer(handle);
if (excludeLayer != nullptr) {
excludeLayers.emplace(excludeLayer);
} else {
@@ -6436,7 +6376,8 @@
return BAD_VALUE;
}
- Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height);
+ Rect layerStackSpaceRect(crop.left, crop.top, crop.left + reqSize.width,
+ crop.top + reqSize.height);
bool childrenOnly = args.childrenOnly;
RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
@@ -6542,37 +6483,40 @@
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
- auto future = mScheduler->schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable
- -> ftl::SharedFuture<FenceResult> {
- ScreenCaptureResults captureResults;
- std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
- captureListener->onScreenCaptureCompleted(captureResults);
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
-
- ftl::SharedFuture<FenceResult> renderFuture;
- renderArea->render([&] {
- renderFuture =
- renderScreenImpl(*renderArea, traverseLayers, buffer, canCaptureBlackoutContent,
- regionSampling, grayscale, captureResults);
- });
-
- if (captureListener) {
- // Defer blocking on renderFuture back to the Binder thread.
- return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
- captureResults.fenceResult = std::move(fenceResult);
+ auto future = mScheduler->schedule(
+ [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
+ kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
+ ScreenCaptureResults captureResults;
+ std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ if (captureListener) {
+ captureResults.fenceResult = base::unexpected(NO_MEMORY);
captureListener->onScreenCaptureCompleted(captureResults);
- return base::unexpected(NO_ERROR);
- })
- .share();
- }
- return renderFuture;
- });
+ }
+ return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
+ }
+
+ ftl::SharedFuture<FenceResult> renderFuture;
+ renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ renderFuture = renderScreenImpl(std::move(renderArea), traverseLayers, buffer,
+ canCaptureBlackoutContent, regionSampling,
+ grayscale, captureResults);
+ });
+
+ if (captureListener) {
+ // Defer blocking on renderFuture back to the Binder thread.
+ return ftl::Future(std::move(renderFuture))
+ .then([captureListener, captureResults = std::move(captureResults)](
+ FenceResult fenceResult) mutable -> FenceResult {
+ captureResults.fenceResult = std::move(fenceResult);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ return base::unexpected(NO_ERROR);
+ })
+ .share();
+ }
+ return renderFuture;
+ });
// Flatten nested futures.
auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
@@ -6583,19 +6527,19 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+ std::unique_ptr<RenderArea> renderArea, TraverseLayersFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
+ size_t layerCount = 0;
traverseLayers([&](Layer* layer) {
+ layerCount++;
captureResults.capturedSecureLayers =
captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
});
- const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
-
// 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.
@@ -6605,13 +6549,13 @@
}
captureResults.buffer = buffer->getBuffer();
- auto dataspace = renderArea.getReqDataSpace();
- auto parent = renderArea.getParentLayer();
+ auto dataspace = renderArea->getReqDataSpace();
+ auto parent = renderArea->getParentLayer();
auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
- if ((dataspace == ui::Dataspace::UNKNOWN) && (parent != nullptr)) {
+ if (dataspace == ui::Dataspace::UNKNOWN && parent) {
Mutex::Autolock lock(mStateLock);
auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
return display.getLayerStack() == layerStack;
@@ -6621,125 +6565,113 @@
display = getDefaultDisplayDeviceLocked();
}
- const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
- dataspace = pickDataspaceFromColorMode(colorMode);
+ dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
renderIntent = display->getCompositionDisplay()->getState().renderIntent;
sdrWhitePointNits = display->getCompositionDisplay()->getState().sdrWhitePointNits;
displayBrightnessNits = display->getCompositionDisplay()->getState().displayBrightnessNits;
}
captureResults.capturedDataspace = dataspace;
- const auto reqWidth = renderArea.getReqWidth();
- const auto reqHeight = renderArea.getReqHeight();
- const auto sourceCrop = renderArea.getSourceCrop();
- const auto transform = renderArea.getTransform();
- const auto rotation = renderArea.getRotationFlags();
- const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect();
+ const auto transform = renderArea->getTransform();
+ const auto display = renderArea->getDisplayDevice();
- renderengine::DisplaySettings clientCompositionDisplay;
- std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
-
- // assume that bounds are never offset, and that they are the same as the
- // buffer bounds.
- clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
- clientCompositionDisplay.clip = sourceCrop;
- clientCompositionDisplay.orientation = rotation;
-
- clientCompositionDisplay.outputDataspace = dataspace;
- clientCompositionDisplay.currentLuminanceNits = displayBrightnessNits;
- clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
- clientCompositionDisplay.renderIntent =
- static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(renderIntent);
-
- const float colorSaturation = grayscale ? 0 : 1;
- clientCompositionDisplay.colorTransform = calculateColorMatrix(colorSaturation);
-
- const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
-
- compositionengine::LayerFE::LayerSettings fillLayer;
- fillLayer.source.buffer.buffer = nullptr;
- fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
- fillLayer.geometry.boundaries =
- FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
- fillLayer.alpha = half(alpha);
- clientCompositionLayers.push_back(fillLayer);
-
- const auto display = renderArea.getDisplayDevice();
- std::vector<Layer*> renderedLayers;
- bool disableBlurs = false;
+ std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+ layers.reserve(layerCount);
+ std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
traverseLayers([&](Layer* layer) {
+ captureResults.capturedHdrLayers |= isHdrLayer(layer);
// Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
// LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
// generated with the layer's current buffer and geometry.
layer->updateSnapshot(true /* updateGeometry */);
- disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
+ layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
- Region clip(renderArea.getBounds());
- compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
- clip,
- layer->needsFilteringForScreenshots(display.get(), transform) ||
- renderArea.needsFiltering(),
- renderArea.isSecure(),
- useProtected,
- layerStackSpaceRect,
- clientCompositionDisplay.outputDataspace,
- true, /* realContentIsVisible */
- false, /* clearContent */
- disableBlurs ? compositionengine::LayerFE::ClientCompositionTargetSettings::
- BlurSetting::Disabled
- : compositionengine::LayerFE::ClientCompositionTargetSettings::
- BlurSetting::Enabled,
- isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits,
+ sp<LayerFE>& layerFE = layers.back().second;
- };
- std::optional<compositionengine::LayerFE::LayerSettings> settings =
- layer->prepareClientComposition(targetSettings);
- if (!settings) {
- return;
+ layerFE->mSnapshot->geomLayerTransform =
+ renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+
+ if (layer->needsFilteringForScreenshots(display.get(), transform)) {
+ filterForScreenshot.insert(layerFE.get());
}
-
- settings->geometry.positionTransform =
- transform.asMatrix4() * settings->geometry.positionTransform;
- // There's no need to process blurs when we're executing region sampling,
- // we're just trying to understand what we're drawing, and doing so without
- // blurs is already a pretty good approximation.
- if (regionSampling) {
- settings->backgroundBlurRadius = 0;
- settings->blurRegions.clear();
- }
- captureResults.capturedHdrLayers |= isHdrLayer(layer);
-
- clientCompositionLayers.push_back(std::move(*settings));
- renderedLayers.push_back(layer);
});
- std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
- clientRenderEngineLayers.reserve(clientCompositionLayers.size());
- std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
- std::back_inserter(clientRenderEngineLayers),
- [](compositionengine::LayerFE::LayerSettings& settings)
- -> renderengine::LayerSettings { return settings; });
-
- // Use an empty fence for the buffer fence, since we just created the buffer so
- // there is no need for synchronization with the GPU.
- base::unique_fd bufferFence;
- getRenderEngine().useProtectedContext(useProtected);
-
- constexpr bool kUseFramebufferCache = false;
- const auto future = getRenderEngine()
- .drawLayers(clientCompositionDisplay, clientRenderEngineLayers,
- buffer, kUseFramebufferCache, std::move(bufferFence))
- .share();
-
- for (auto* layer : renderedLayers) {
- layer->onLayerDisplayed(future);
+ ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
+ if (!layers.empty()) {
+ const sp<LayerFE>& layerFE = layers.back().second;
+ layerStack = layerFE->getCompositionState()->outputFilter.layerStack;
}
- // Always switch back to unprotected context.
- getRenderEngine().useProtectedContext(false);
+ auto copyLayerFEs = [&layers]() {
+ std::vector<sp<compositionengine::LayerFE>> layerFEs;
+ layerFEs.reserve(layers.size());
+ for (const auto& [_, layerFE] : layers) {
+ layerFEs.push_back(layerFE);
+ }
+ return layerFEs;
+ };
- return future;
+ auto present = [this, buffer = std::move(buffer), dataspace, sdrWhitePointNits,
+ displayBrightnessNits, filterForScreenshot = std::move(filterForScreenshot),
+ grayscale, layerFEs = copyLayerFEs(), layerStack, regionSampling,
+ renderArea = std::move(renderArea), renderIntent]() -> FenceResult {
+ std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
+ mFactory.createCompositionEngine();
+ compositionEngine->setRenderEngine(mRenderEngine.get());
+
+ compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace,
+ .renderIntent = renderIntent};
+
+ std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
+ ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine,
+ .colorProfile = colorProfile,
+ .renderArea = *renderArea,
+ .layerStack = layerStack,
+ .buffer = std::move(buffer),
+ .sdrWhitePointNits = sdrWhitePointNits,
+ .displayBrightnessNits = displayBrightnessNits,
+ .filterForScreenshot = std::move(filterForScreenshot),
+ .regionSampling = regionSampling});
+
+ const float colorSaturation = grayscale ? 0 : 1;
+ compositionengine::CompositionRefreshArgs refreshArgs{
+ .outputs = {output},
+ .layers = std::move(layerFEs),
+ .updatingOutputGeometryThisFrame = true,
+ .updatingGeometryThisFrame = true,
+ .colorTransformMatrix = calculateColorMatrix(colorSaturation),
+ };
+ compositionEngine->present(refreshArgs);
+
+ return output->getRenderSurface()->getClientTargetAcquireFence();
+ };
+
+ // If RenderEngine is threaded, we can safely call CompositionEngine::present off the main
+ // thread as the RenderEngine::drawLayers call will run on RenderEngine's thread. Otherwise,
+ // we need RenderEngine to run on the main thread so we call CompositionEngine::present
+ // immediately.
+ //
+ // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
+ // to CompositionEngine::present.
+ const bool renderEngineIsThreaded = [&]() {
+ using Type = renderengine::RenderEngine::RenderEngineType;
+ const auto type = mRenderEngine->getRenderEngineType();
+ return type == Type::THREADED || type == Type::SKIA_GL_THREADED;
+ }();
+ auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share()
+ : ftl::yield(present()).share();
+
+ for (auto& [layer, layerFE] : layers) {
+ layer->onLayerDisplayed(
+ ftl::Future(presentFuture)
+ .then([layerFE = std::move(layerFE)](FenceResult) {
+ return layerFE->stealCompositionResult().releaseFences.back().get();
+ })
+ .share());
+ }
+
+ return presentFuture;
}
// ---------------------------------------------------------------------------
@@ -6780,10 +6712,10 @@
}
}
-std::optional<DisplayModePtr> SurfaceFlinger::getPreferredDisplayMode(
+ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
- schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) {
+ schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) {
return schedulerMode;
}
@@ -6791,12 +6723,17 @@
.transform(&PhysicalDisplay::snapshotRef)
.and_then([&](const display::DisplaySnapshot& snapshot) {
return snapshot.displayModes().get(defaultModeId);
+ })
+ .transform([](const DisplayModePtr& modePtr) {
+ return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
});
}
status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
const sp<DisplayDevice>& display,
- const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+ const scheduler::RefreshRateSelector::PolicyVariant& policy) {
+ const auto displayId = display->getPhysicalId();
+
Mutex::Autolock lock(mStateLock);
if (mDebugDisplayModeSetByBackdoor) {
@@ -6804,62 +6741,88 @@
return NO_ERROR;
}
- const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
- if (setPolicyResult < 0) {
- return BAD_VALUE;
- }
- if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
- return NO_ERROR;
- }
+ auto& selector = display->refreshRateSelector();
+ using SetPolicyResult = scheduler::RefreshRateSelector::SetPolicyResult;
- const scheduler::RefreshRateConfigs::Policy currentPolicy =
- display->refreshRateConfigs().getCurrentPolicy();
+ switch (selector.setPolicy(policy)) {
+ case SetPolicyResult::Invalid:
+ return BAD_VALUE;
+ case SetPolicyResult::Unchanged:
+ return NO_ERROR;
+ case SetPolicyResult::Changed:
+ return applyRefreshRateSelectorPolicy(displayId, selector);
+ }
+}
+status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
+ PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) {
+ const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
// TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
// be depending in this callback.
- const auto activeModePtr = display->refreshRateConfigs().getActiveModePtr();
- if (isDisplayActiveLocked(display)) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
+ if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
toggleKernelIdleTimer();
} else {
- mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
+ mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
}
- auto preferredModeOpt =
- getPreferredDisplayMode(display->getPhysicalId(), currentPolicy.defaultMode);
+ auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
if (!preferredModeOpt) {
ALOGE("%s: Preferred mode is unknown", __func__);
return NAME_NOT_FOUND;
}
auto preferredMode = std::move(*preferredModeOpt);
- const auto preferredModeId = preferredMode->getId();
+ const auto preferredModeId = preferredMode.modePtr->getId();
ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
- to_string(preferredMode->getFps()).c_str());
+ to_string(preferredMode.fps).c_str());
- if (!display->refreshRateConfigs().isModeAllowed(preferredModeId)) {
+ if (!selector.isModeAllowed(preferredMode)) {
ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), DisplayModeEvent::Changed});
+ setDesiredActiveMode({std::move(preferredMode), .emitEvent = true});
return NO_ERROR;
}
-status_t SurfaceFlinger::setDesiredDisplayModeSpecs(
- const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
- float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+namespace {
+FpsRange translate(const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& aidlRange) {
+ return FpsRange{Fps::fromValue(aidlRange.min), Fps::fromValue(aidlRange.max)};
+}
+
+FpsRanges translate(const gui::DisplayModeSpecs::RefreshRateRanges& aidlRanges) {
+ return FpsRanges{translate(aidlRanges.physical), translate(aidlRanges.render)};
+}
+
+gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange translate(const FpsRange& range) {
+ gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange aidlRange;
+ aidlRange.min = range.min.getValue();
+ aidlRange.max = range.max.getValue();
+ return aidlRange;
+}
+
+gui::DisplayModeSpecs::RefreshRateRanges translate(const FpsRanges& ranges) {
+ gui::DisplayModeSpecs::RefreshRateRanges aidlRanges;
+ aidlRanges.physical = translate(ranges.physical);
+ aidlRanges.render = translate(ranges.render);
+ return aidlRanges;
+}
+
+} // namespace
+
+status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ const gui::DisplayModeSpecs& specs) {
ATRACE_CALL();
if (!displayToken) {
return BAD_VALUE;
}
- auto future = mScheduler->schedule([=]() -> status_t {
+ auto future = mScheduler->schedule([=]() 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",
@@ -6869,16 +6832,11 @@
ALOGW("Attempt to set desired display modes for virtual display");
return INVALID_OPERATION;
} else {
- using Policy = scheduler::RefreshRateConfigs::Policy;
- const Policy policy{DisplayModeId(defaultMode),
- allowGroupSwitching,
- {Fps::fromValue(primaryRefreshRateMin),
- Fps::fromValue(primaryRefreshRateMax)},
- {Fps::fromValue(appRequestRefreshRateMin),
- Fps::fromValue(appRequestRefreshRateMax)}};
- constexpr bool kOverridePolicy = false;
+ using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy;
+ const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges),
+ translate(specs.appRequestRanges), specs.allowGroupSwitching};
- return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+ return setDesiredDisplayModeSpecsInternal(display, policy);
}
});
@@ -6886,16 +6844,10 @@
}
status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
- ui::DisplayModeId* outDefaultMode,
- bool* outAllowGroupSwitching,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
+ gui::DisplayModeSpecs* outSpecs) {
ATRACE_CALL();
- if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin ||
- !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+ if (!displayToken || !outSpecs) {
return BAD_VALUE;
}
@@ -6909,21 +6861,15 @@
return INVALID_OPERATION;
}
- scheduler::RefreshRateConfigs::Policy policy =
- display->refreshRateConfigs().getDisplayManagerPolicy();
- *outDefaultMode = policy.defaultMode.value();
- *outAllowGroupSwitching = policy.allowGroupSwitching;
- *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
- *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
- *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue();
- *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue();
+ scheduler::RefreshRateSelector::Policy policy =
+ display->refreshRateSelector().getDisplayManagerPolicy();
+ outSpecs->defaultMode = policy.defaultMode.value();
+ outSpecs->allowGroupSwitching = policy.allowGroupSwitching;
+ outSpecs->primaryRanges = translate(policy.primaryRanges);
+ outSpecs->appRequestRanges = translate(policy.appRequestRanges);
return NO_ERROR;
}
-wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
- return Layer::fromHandle(handle);
-}
-
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
mNumLayers++;
if (!layer->isRemovedFromCurrentState()) {
@@ -7007,23 +6953,13 @@
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+ device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate);
}
}
}
}
-status_t SurfaceFlinger::addTransactionTraceListener(
- const sp<gui::ITransactionTraceListener>& listener) {
- if (!listener) {
- return BAD_VALUE;
- }
-
- mInterceptor->addTransactionTraceListener(listener);
-
- return NO_ERROR;
-}
-
int SurfaceFlinger::getGpuContextPriority() {
return getRenderEngine().getContextPriority();
}
@@ -7042,7 +6978,7 @@
if (!getHwComposer().isHeadless()) {
if (const auto display = getDefaultDisplayDevice()) {
- maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+ maxRefreshRate = display->refreshRateSelector().getSupportedRefreshRateRange().max;
}
}
@@ -7057,7 +6993,7 @@
refreshRate = *frameRateOverride;
} else if (!getHwComposer().isHeadless()) {
if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
- refreshRate = display->refreshRateConfigs().getActiveModePtr()->getFps();
+ refreshRate = display->refreshRateSelector().getActiveMode().fps;
}
}
@@ -7099,11 +7035,23 @@
parent->addChild(layer);
}
- layer->updateTransformHint(mActiveDisplayTransformHint);
+ ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current);
+ sp<const DisplayDevice> hintDisplay;
+ // Find the display that includes the layer.
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getLayerStack() == layerStack) {
+ hintDisplay = display;
+ break;
+ }
+ }
+
+ if (hintDisplay) {
+ layer->updateTransformHint(hintDisplay->getTransformHint());
+ }
+
if (mTransactionTracing) {
mTransactionTracing->onLayerAddedToDrawingState(layer->getSequence(), vsyncId.value);
}
- mInterceptor->saveSurfaceCreation(layer);
}
void SurfaceFlinger::sample() {
@@ -7119,28 +7067,29 @@
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
}
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay) {
ATRACE_CALL();
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
- display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+ if (inactiveDisplay) {
+ inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
}
- if (!activeDisplay) {
- ALOGE("%s: activeDisplay is null", __func__);
- return;
- }
- mActiveDisplayToken = activeDisplay->getDisplayToken();
+ mActiveDisplayId = activeDisplay->getPhysicalId();
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
+
updateInternalDisplayVsyncLocked(activeDisplay);
mScheduler->setModeChangePending(false);
- mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
+ mScheduler->setLeaderDisplay(mActiveDisplayId);
+
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay->getTransformHint();
- // Update the kernel timer for the current active display, since the policy
- // for this display might have changed when it was not the active display.
- toggleKernelIdleTimer();
+ // The policy of the new active/leader 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.
+ applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector());
}
status_t SurfaceFlinger::addWindowInfosListener(
@@ -7156,34 +7105,53 @@
}
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
- const BufferData& bufferData, const char* layerName) const {
- bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
- bool bufferSizeExceedsLimit = false;
- std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
- if (cacheIdChanged && bufferData.buffer != nullptr) {
- bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
- bufferData.buffer->getHeight());
- if (!bufferSizeExceedsLimit) {
- ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
- buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+ BufferData& bufferData, const char* layerName, uint64_t transactionId) {
+ if (bufferData.buffer &&
+ exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), bufferData.buffer->getHeight())) {
+ std::string errorMessage =
+ base::StringPrintf("Attempted to create an ExternalTexture with size (%u, %u) for "
+ "layer %s that exceeds render target size limit of %u.",
+ bufferData.buffer->getWidth(), bufferData.buffer->getHeight(),
+ layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
+ ALOGD("%s", errorMessage.c_str());
+ if (bufferData.releaseBufferListener) {
+ bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage);
}
- } else if (cacheIdChanged) {
- buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
- } else if (bufferData.buffer != nullptr) {
- bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
- bufferData.buffer->getHeight());
- if (!bufferSizeExceedsLimit) {
- buffer = std::make_shared<
- renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::
- Usage::READABLE);
- }
+ return nullptr;
}
- ALOGE_IF(bufferSizeExceedsLimit,
- "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
- "limit.",
- layerName);
- return buffer;
+
+ bool cachedBufferChanged =
+ bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
+ if (cachedBufferChanged && bufferData.buffer) {
+ auto result = ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
+ if (result.ok()) {
+ return result.value();
+ }
+
+ if (result.error() == ClientCache::AddError::CacheFull) {
+ ALOGE("Attempted to create an ExternalTexture for layer %s but CacheFull", layerName);
+
+ if (bufferData.releaseBufferListener) {
+ bufferData.releaseBufferListener->onTransactionQueueStalled(
+ "Buffer processing hung due to full buffer cache");
+ }
+ }
+
+ return nullptr;
+ }
+
+ if (cachedBufferChanged) {
+ return ClientCache::getInstance().get(bufferData.cachedBuffer);
+ }
+
+ if (bufferData.buffer) {
+ return std::make_shared<
+ renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::Usage::
+ READABLE);
+ }
+
+ return nullptr;
}
bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) {
@@ -7201,7 +7169,7 @@
for (const auto& mirrorDisplay : mirrorDisplays) {
// Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display
// accidentally.
- sp<Layer> rootMirrorLayer = Layer::fromHandle(mirrorDisplay.rootHandle).promote();
+ sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle);
rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
for (const auto& layer : mDrawingState.layersSortedByZ) {
if (layer->getLayerStack() != mirrorDisplay.layerStack ||
@@ -7339,6 +7307,10 @@
binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId,
sp<IBinder>* outDisplay) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
*outDisplay = mFlinger->getPhysicalDisplayToken(*id);
return binder::Status::ok();
@@ -7389,11 +7361,12 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(const sp<IBinder>& display,
+binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) {
using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
ui::StaticDisplayInfo info;
- status_t status = mFlinger->getStaticDisplayInfo(display, &info);
+
+ status_t status = mFlinger->getStaticDisplayInfo(displayId, &info);
if (status == NO_ERROR) {
// convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
@@ -7432,53 +7405,71 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo) {
+ // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
+ for (const auto& mode : info.supportedDisplayModes) {
+ gui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const ui::Hdr& value) { return static_cast<int32_t>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = info.activeDisplayModeId;
+ outInfo->renderFrameRate = info.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
+ for (const auto& cmode : info.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
+
+ gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
+ hdrCapabilities.supportedHdrTypes.clear();
+ hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size());
+ for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
+ hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
+ }
+ hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
+ hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
+ hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
+
+ outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken(
+ const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) {
ui::DynamicDisplayInfo info;
- status_t status = mFlinger->getDynamicDisplayInfo(display, &info);
+ status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info);
if (status == NO_ERROR) {
- // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
- for (const auto& mode : info.supportedDisplayModes) {
- gui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(info, outInfo);
+ }
+ return binderStatusFromStatusT(status);
+}
- outInfo->activeDisplayModeId = info.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
- for (const auto& cmode : info.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
-
- gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
- hdrCapabilities.supportedHdrTypes.clear();
- hdrCapabilities.supportedHdrTypes.reserve(
- info.hdrCapabilities.getSupportedHdrTypes().size());
- for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
- hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
- }
- hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
- hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
- hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
-
- outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) {
+ ui::DynamicDisplayInfo info;
+ status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info);
+ if (status == NO_ERROR) {
+ getDynamicDisplayInfoInternal(info, outInfo);
}
return binderStatusFromStatusT(status);
}
@@ -7532,6 +7523,14 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::getOverlaySupport(gui::OverlayProperties* outProperties) {
+ status_t status = checkAccessPermission();
+ if (status == OK) {
+ status = mFlinger->getOverlaySupport(outProperties);
+ }
+ return binderStatusFromStatusT(status);
+}
+
binder::Status SurfaceComposerAIDL::getBootDisplayModeSupport(bool* outMode) {
status_t status = checkAccessPermission();
if (status == OK) {
@@ -7612,30 +7611,6 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::enableVSyncInjections(bool enable) {
- if (!mFlinger->hasMockHwc()) {
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
-
- status_t status = checkAccessPermission();
- if (status == OK) {
- status = mFlinger->enableVSyncInjections(enable);
- }
- return binderStatusFromStatusT(status);
-}
-
-binder::Status SurfaceComposerAIDL::injectVSync(int64_t when) {
- if (!mFlinger->hasMockHwc()) {
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
-
- status_t status = checkAccessPermission();
- if (status == OK) {
- status = mFlinger->injectVSync(when);
- }
- return binderStatusFromStatusT(status);
-}
-
binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
if (!outLayers) {
return binderStatusFromStatusT(UNEXPECTED_NULL);
@@ -7816,18 +7791,11 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(
- const sp<IBinder>& displayToken, int32_t defaultMode, bool allowGroupSwitching,
- float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ const gui::DisplayModeSpecs& specs) {
status_t status = checkAccessPermission();
if (status == OK) {
- status = mFlinger->setDesiredDisplayModeSpecs(displayToken,
- static_cast<ui::DisplayModeId>(defaultMode),
- allowGroupSwitching, primaryRefreshRateMin,
- primaryRefreshRateMax,
- appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ status = mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
}
return binderStatusFromStatusT(status);
}
@@ -7843,25 +7811,7 @@
return binderStatusFromStatusT(status);
}
- ui::DisplayModeId displayModeId;
- bool allowGroupSwitching;
- float primaryRefreshRateMin;
- float primaryRefreshRateMax;
- float appRequestRefreshRateMin;
- float appRequestRefreshRateMax;
- status = mFlinger->getDesiredDisplayModeSpecs(displayToken, &displayModeId,
- &allowGroupSwitching, &primaryRefreshRateMin,
- &primaryRefreshRateMax, &appRequestRefreshRateMin,
- &appRequestRefreshRateMax);
- if (status == NO_ERROR) {
- outSpecs->defaultMode = displayModeId;
- outSpecs->allowGroupSwitching = allowGroupSwitching;
- outSpecs->primaryRefreshRateMin = primaryRefreshRateMin;
- outSpecs->primaryRefreshRateMax = primaryRefreshRateMax;
- outSpecs->appRequestRefreshRateMin = appRequestRefreshRateMin;
- outSpecs->appRequestRefreshRateMax = appRequestRefreshRateMax;
- }
-
+ status = mFlinger->getDesiredDisplayModeSpecs(displayToken, outSpecs);
return binderStatusFromStatusT(status);
}
@@ -7955,19 +7905,6 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::addTransactionTraceListener(
- const sp<gui::ITransactionTraceListener>& listener) {
- 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) {
- status = mFlinger->addTransactionTraceListener(listener);
- } else {
- status = PERMISSION_DENIED;
- }
- return binderStatusFromStatusT(status);
-}
-
binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
*outPriority = mFlinger->getGpuContextPriority();
return binder::Status::ok();
@@ -8016,7 +7953,7 @@
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) &&
+ if ((uid != AID_GRAPHICS) && (uid != AID_SYSTEM) &&
!PermissionCache::checkPermission(sControlDisplayBrightness, pid, uid)) {
ALOGE("Permission Denial: can't control brightness pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 85d11ba..c957b67 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -26,15 +26,17 @@
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <ftl/future.h>
+#include <ftl/non_null.h>
#include <gui/BufferQueue.h>
#include <gui/CompositorTiming.h>
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerDebugInfo.h>
+
#include <gui/LayerState.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
@@ -57,7 +59,6 @@
#include <scheduler/Time.h>
#include <ui/FenceResult.h>
-#include "ClientCache.h"
#include "Display/DisplayMap.h"
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
@@ -66,16 +67,16 @@
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FlagManager.h"
-#include "FrameTracker.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
-#include "LocklessQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "ThreadContext.h"
-#include "TracedOrdinal.h"
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
@@ -96,6 +97,7 @@
#include <unordered_map>
#include <unordered_set>
#include <utility>
+#include <vector>
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include "Client.h"
@@ -121,10 +123,13 @@
class ScreenCapturer;
class WindowInfosListenerInvoker;
+using frontend::TransactionHandler;
using gui::CaptureArgs;
using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
+using gui::ITransactionCompletedListener;
using gui::LayerCaptureArgs;
+
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -214,10 +219,6 @@
static uint32_t maxGraphicsWidth;
static uint32_t maxGraphicsHeight;
- // Indicate if a device has wide color gamut display. This is typically
- // found on devices with wide color gamut (e.g. Display-P3) display.
- static bool hasWideColorDisplay;
-
// Indicate if device wants color management on its display.
static const constexpr bool useColorManagement = true;
@@ -278,6 +279,11 @@
void removeHierarchyFromOffscreenLayers(Layer* layer);
void removeFromOffscreenLayers(Layer* layer);
+ // Called when all clients have released all their references to
+ // this layer. The layer may still be kept alive by its parents but
+ // the client can no longer modify this layer directly.
+ void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
+
// TODO: Remove atomic if move dtor to main thread CL lands
std::atomic<uint32_t> mNumClones;
@@ -285,12 +291,6 @@
return mTransactionCallbackInvoker;
}
- // Converts from a binder handle to a Layer
- // Returns nullptr if the handle does not point to an existing layer.
- // Otherwise, returns a weak reference so that callers off the main-thread
- // won't accidentally hold onto the last strong reference.
- wp<Layer> fromHandle(const sp<IBinder>& handle) const;
-
// If set, disables reusing client composition buffers. This can be set by
// debug.sf.disable_client_composition_cache
bool mDisableClientCompositionCache = false;
@@ -319,7 +319,7 @@
REQUIRES(mStateLock);
virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
- const BufferData& bufferData, const char* layerName) const;
+ BufferData& bufferData, const char* layerName, uint64_t transactionId);
// Returns true if any display matches a `bool(const DisplayDevice&)` predicate.
template <typename Predicate>
@@ -341,6 +341,7 @@
friend class RegionSamplingThread;
friend class LayerRenderArea;
friend class LayerTracing;
+ friend class SurfaceComposerAIDL;
// For unit tests
friend class TestableSurfaceFlinger;
@@ -435,10 +436,6 @@
mCounterByLayerHandle GUARDED_BY(mLock);
};
- using ActiveModeInfo = DisplayDevice::ActiveModeInfo;
- using KernelIdleTimerController =
- ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController;
-
enum class BootStage {
BOOTLOADER,
BOOTANIMATION,
@@ -473,8 +470,7 @@
typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
void modulateVsync(Handler handler, Args... args) {
if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs();
- setVsyncConfig(*config, vsyncPeriod);
+ setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod());
}
}
@@ -499,9 +495,8 @@
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
+ Vector<ComposerState>& state, const Vector<DisplayState>& displays,
+ uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
@@ -521,23 +516,25 @@
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
EXCLUDES(mStateLock);
- status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
+ status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*)
EXCLUDES(mStateLock);
- status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
- EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo*) EXCLUDES(mStateLock);
+ void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&,
+ const display::DisplaySnapshot&);
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
status_t getBootDisplayModeSupport(bool* outSupport) const;
status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId);
+ status_t getOverlaySupport(gui::OverlayProperties* outProperties) const;
status_t clearBootDisplayMode(const sp<IBinder>& displayToken);
void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
void setGameContentType(const sp<IBinder>& displayToken, bool on);
void setPowerMode(const sp<IBinder>& displayToken, int mode);
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes);
- status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success);
- status_t enableVSyncInjections(bool enable);
- status_t injectVSync(nsecs_t when);
+ status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
status_t getColorManagement(bool* outGetColorManagement) const;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
@@ -561,17 +558,8 @@
status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener);
status_t removeTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener);
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
- ui::DisplayModeId displayModeId, bool allowGroupSwitching,
- float primaryRefreshRateMin, float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax);
- status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
- ui::DisplayModeId* outDefaultMode,
- bool* outAllowGroupSwitching,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax);
+ const gui::DisplayModeSpecs&);
+ status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs*);
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const;
status_t setDisplayBrightness(const sp<IBinder>& displayToken,
const gui::DisplayBrightness& brightness);
@@ -594,8 +582,6 @@
status_t setOverrideFrameRate(uid_t uid, float frameRate);
- status_t addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener);
-
int getGpuContextPriority();
status_t getMaxAcquiredBufferCount(int* buffers) const;
@@ -636,16 +622,17 @@
// ISchedulerCallback overrides:
// Toggles hardware VSYNC by calling into HWC.
+ // TODO(b/241286146): Rename for self-explanatory API.
void setVsyncEnabled(bool) override;
- // Sets the desired display mode if allowed by policy.
- void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override;
- // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+ void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
- // Called when the frame rate override list changed to trigger an event.
void triggerOnFrameRateOverridesChanged() override;
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer() REQUIRES(mStateLock);
+
+ using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
+
// Get the controller and timeout that will help decide how the kernel idle timer will be
// configured and what value to use as the timeout.
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
@@ -659,9 +646,11 @@
bool mKernelIdleTimerEnabled = false;
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
+ // Show render rate with refresh rate overlay
+ bool mRefreshRateOverlayRenderRate = false;
- // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
- void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
+ void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
// Sets the active mode and a new refresh rate in SF.
void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
@@ -680,15 +669,17 @@
// Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
// display. Falls back to the display's defaultModeId otherwise.
- std::optional<DisplayModePtr> getPreferredDisplayMode(PhysicalDisplayId,
- DisplayModeId defaultModeId) const
- REQUIRES(mStateLock);
+ ftl::Optional<scheduler::FrameRateMode> getPreferredDisplayMode(
+ PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock);
- // Sets the desired display mode specs.
status_t setDesiredDisplayModeSpecsInternal(
- const sp<DisplayDevice>& display,
- const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
- EXCLUDES(mStateLock);
+ const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
+ EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+
+ // TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
+ status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
+ const scheduler::RefreshRateSelector&)
+ REQUIRES(mStateLock, kMainThreadContext);
void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
void commitTransactionsLocked(uint32_t transactionFlags)
@@ -708,7 +699,7 @@
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
- void initScheduler(const sp<const DisplayDevice>&) REQUIRES(mStateLock);
+ void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
@@ -716,8 +707,9 @@
/*
* Transactions
*/
- bool applyTransactionState(const FrameTimelineInfo& info, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
+ bool applyTransactionState(const FrameTimelineInfo& info,
+ std::vector<ResolvedComposerState>& state,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
@@ -729,15 +721,18 @@
bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
+ void addTransactionReadyFilters();
+ TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
+ const TransactionHandler::TransactionFlushState& flushState)
+ REQUIRES(kMainThreadContext);
+ TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
+ const TransactionHandler::TransactionFlushState& flushState)
+ REQUIRES(kMainThreadContext);
- int flushPendingTransactionQueues(
- std::vector<TransactionState>& transactions,
- std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
- bool tryApplyUnsignaled) REQUIRES(mStateLock) REQUIRES(kMainThreadContext);
-
- uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
+ uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
+ int64_t postTime, uint32_t permissions, uint64_t transactionId)
+ REQUIRES(mStateLock);
uint32_t getTransactionFlags() const;
@@ -751,23 +746,9 @@
void commitOffscreenLayers();
- enum class TransactionReadiness {
- NotReady,
- NotReadyBarrier,
- Ready,
- ReadyUnsignaled,
- };
- TransactionReadiness transactionIsReadyToBeApplied(
- TransactionState&, const FrameTimelineInfo&, bool isAutoTimestamp,
- TimePoint desiredPresentTime, uid_t originUid, const Vector<ComposerState>&,
- const std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>&
- bufferLayersReadyToPresent,
- size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock)
- REQUIRES(kMainThreadContext);
-
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
- size_t totalTXapplied) const;
+ bool firstTransaction) const;
bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -778,10 +759,7 @@
/*
* Layer management
*/
- status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
- const sp<IBinder>& parentHandle, int32_t* outLayerId,
- const sp<Layer>& parentLayer = nullptr,
- uint32_t* outTransformHint = nullptr);
+ status_t createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult);
status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
sp<Layer>* outLayer);
@@ -790,20 +768,16 @@
sp<Layer>* outLayer);
status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle, int32_t* outLayerId);
+ gui::CreateSurfaceResult& outResult);
status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
- sp<IBinder>* outHandle, int32_t* outLayerId);
+ gui::CreateSurfaceResult& outResult);
- // called when all clients have released all their references to
- // this layer meaning it is entirely safe to destroy all
- // resources associated to this layer.
- void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
void markLayerPendingRemovalLocked(const sp<Layer>& layer);
// add a layer to SurfaceFlinger
- status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
- const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot,
+ status_t addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle,
+ const sp<Layer>& layer, const wp<Layer>& parentLayer,
uint32_t* outTransformHint);
// Traverse through all the layers and compute and cache its bounds.
@@ -821,9 +795,10 @@
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- const RenderArea&, TraverseLayersFunction,
+ std::unique_ptr<RenderArea>, TraverseLayersFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
- bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock);
+ bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock)
+ REQUIRES(kMainThreadContext);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
@@ -841,10 +816,6 @@
void initializeDisplays();
void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext);
- bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
- return display->getDisplayToken() == mActiveDisplayToken;
- }
-
sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken);
@@ -879,12 +850,12 @@
}
sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+ if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
return display;
}
// The active display is outdated, so fall back to the primary display.
- mActiveDisplayToken.clear();
- return getDisplayDeviceLocked(getPrimaryDisplayTokenLocked());
+ mActiveDisplayId = getPrimaryDisplayIdLocked();
+ return getDisplayDeviceLocked(mActiveDisplayId);
}
sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) {
@@ -972,7 +943,8 @@
const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
- void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
+ void processDisplayRemoved(const wp<IBinder>& displayToken)
+ REQUIRES(mStateLock, kMainThreadContext);
void processDisplayChanged(const wp<IBinder>& displayToken,
const DisplayDeviceState& currentState,
const DisplayDeviceState& drawingState)
@@ -1037,7 +1009,10 @@
VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
- void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+ // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr`
+ // in tests, and `activeDisplay` must not be `nullptr` as a precondition.
+ void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
@@ -1057,7 +1032,9 @@
void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
- void dumpVSync(std::string& result) const REQUIRES(mStateLock);
+ void dumpScheduler(std::string& result) const REQUIRES(mStateLock);
+ void dumpEvents(std::string& result) const REQUIRES(mStateLock);
+ void dumpVsync(std::string& result) const REQUIRES(mStateLock);
void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock);
void dumpDisplays(std::string& result) const REQUIRES(mStateLock);
@@ -1094,20 +1071,12 @@
status_t CheckTransactCodeCredentials(uint32_t code);
// Add transaction to the Transaction Queue
- void queueTransaction(TransactionState& state);
- void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
- void signalSynchronousTransactions(const uint32_t flag);
/*
* Generic Layer Metadata
*/
const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
- /*
- * Misc
- */
- std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId) REQUIRES(mStateLock);
-
static int calculateMaxAcquiredBufferCount(Fps refreshRate,
std::chrono::nanoseconds presentLatency);
int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
@@ -1129,10 +1098,13 @@
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
std::atomic<int32_t> mTransactionFlags = 0;
- std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
std::atomic<uint32_t> mUniqueTransactionId = 1;
SortedVector<sp<Layer>> mLayersPendingRemoval;
+ // Buffers that have been discarded by clients and need to be evicted from per-layer caches so
+ // the graphics memory can be immediately freed.
+ std::vector<uint64_t> mBufferIdsToUncache;
+
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
@@ -1153,7 +1125,6 @@
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
- bool mGpuToCpuSupported = false;
bool mIsUserBuild = true;
// Can only accessed from the main thread, these members
@@ -1215,6 +1186,9 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
+ // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+ PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock);
+
struct {
DisplayIdGenerator<GpuVirtualDisplayId> gpu;
std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
@@ -1228,7 +1202,6 @@
bool mLayerCachingEnabled = false;
bool mPropagateBackpressureClientComposition = false;
- sp<SurfaceInterceptor> mInterceptor;
LayerTracing mLayerTracing{*this};
bool mLayerTracingEnabled = false;
@@ -1259,10 +1232,6 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
- std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
- mPendingTransactionQueues;
- LocklessQueue<TransactionState> mLocklessTransactionQueue;
- std::atomic<size_t> mPendingTransactionCount = 0;
std::atomic<size_t> mNumLayers = 0;
// to linkToDeath
@@ -1285,11 +1254,16 @@
// This property can be used to force SurfaceFlinger to always pick a certain color mode.
ui::ColorMode mForceColorMode = ui::ColorMode::NATIVE;
+ // Whether to enable wide color gamut (e.g. Display P3) for internal displays that support it.
+ // If false, wide color modes are filtered out for all internal displays.
+ bool mSupportsWideColor = false;
+
ui::Dataspace mDefaultCompositionDataspace;
ui::Dataspace mWideColorGamutCompositionDataspace;
ui::Dataspace mColorSpaceAgnosticDataspace;
float mDimmingRatio = -1.f;
+ std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
// mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
// any mutex.
@@ -1297,8 +1271,6 @@
const std::string mHwcServiceName;
- bool hasMockHwc() const { return mHwcServiceName == "mock"; }
-
/*
* Scheduler
*/
@@ -1336,8 +1308,8 @@
sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter;
ui::DisplayPrimaries mInternalDisplayPrimaries;
- const float mInternalDisplayDensity;
const float mEmulatedDisplayDensity;
+ const float mInternalDisplayDensity;
// Should only be accessed by the main thread.
sp<os::IInputFlinger> mInputFlinger;
@@ -1400,8 +1372,6 @@
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
}
- wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
-
const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
FlagManager mFlagManager;
@@ -1418,9 +1388,8 @@
bool early = false;
} mPowerHintSessionMode;
- nsecs_t mAnimationTransactionTimeout = s2ns(5);
-
- friend class SurfaceComposerAIDL;
+ TransactionHandler mTransactionHandler;
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1443,16 +1412,19 @@
gui::DisplayStatInfo* outStatInfo) override;
binder::Status getDisplayState(const sp<IBinder>& display,
gui::DisplayState* outState) override;
- binder::Status getStaticDisplayInfo(const sp<IBinder>& display,
+ binder::Status getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) override;
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ gui::DynamicDisplayInfo* outInfo) override;
binder::Status getDisplayNativePrimaries(const sp<IBinder>& display,
gui::DisplayPrimaries* outPrimaries) override;
binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override;
binder::Status setBootDisplayMode(const sp<IBinder>& display, int displayModeId) override;
binder::Status clearBootDisplayMode(const sp<IBinder>& display) override;
binder::Status getBootDisplayModeSupport(bool* outMode) override;
+ binder::Status getOverlaySupport(gui::OverlayProperties* outProperties) override;
binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override;
binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
binder::Status captureDisplay(const DisplayCaptureArgs&,
@@ -1472,8 +1444,6 @@
binder::Status overrideHdrTypes(const sp<IBinder>& display,
const std::vector<int32_t>& hdrTypes) override;
binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override;
- binder::Status enableVSyncInjections(bool enable) override;
- binder::Status injectVSync(int64_t when) override;
binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override;
binder::Status getColorManagement(bool* outGetColorManagement) override;
binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override;
@@ -1499,11 +1469,8 @@
const sp<gui::ITunnelModeEnabledListener>& listener) override;
binder::Status removeTunnelModeEnabledListener(
const sp<gui::ITunnelModeEnabledListener>& listener) override;
- binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, int32_t defaultMode,
- bool allowGroupSwitching, float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) override;
+ binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ const gui::DisplayModeSpecs&) override;
binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
gui::DisplayModeSpecs* outSpecs) override;
binder::Status getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
@@ -1523,8 +1490,6 @@
const sp<IBinder>& displayToken,
std::optional<gui::DisplayDecorationSupport>* outSupport) override;
binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
- binder::Status addTransactionTraceListener(
- const sp<gui::ITransactionTraceListener>& listener) override;
binder::Status getGpuContextPriority(int32_t* outPriority) override;
binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
binder::Status addWindowInfosListener(
@@ -1537,6 +1502,8 @@
status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
status_t checkControlDisplayBrightnessPermission();
status_t checkReadFrameBufferPermission();
+ static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo);
private:
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 3e30dcb..7e6894d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -29,7 +29,6 @@
#include "StartPropertySetThread.h"
#include "SurfaceFlingerDefaultFactory.h"
#include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
#include "FrameTimeline/FrameTimeline.h"
@@ -54,10 +53,6 @@
}
}
-sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
- return sp<android::impl::SurfaceInterceptor>::make();
-}
-
sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
bool timestampPropertyValue) {
return sp<StartPropertySetThread>::make(timestampPropertyValue);
@@ -96,6 +91,10 @@
return sp<Layer>::make(args);
}
+sp<LayerFE> DefaultFactory::createLayerFE(const std::string& layerName) {
+ return sp<LayerFE>::make(layerName);
+}
+
std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
return std::make_unique<FrameTracer>();
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 6fca402..2c6de0e 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,6 @@
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) override;
- sp<SurfaceInterceptor> createSurfaceInterceptor() override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
@@ -43,6 +42,7 @@
std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override;
sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
+ sp<LayerFE> createLayerFE(const std::string& layerName) override;
std::unique_ptr<FrameTracer> createFrameTracer() override;
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6d18ade..f310c4a 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -38,13 +38,12 @@
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
class Layer;
+class LayerFE;
class StartPropertySetThread;
class SurfaceFlinger;
-class SurfaceInterceptor;
class TimeStats;
struct DisplayDeviceCreationArgs;
-struct LayerCreationArgs;
namespace compositionengine {
class CompositionEngine;
@@ -53,7 +52,6 @@
namespace scheduler {
class VsyncConfiguration;
class VsyncController;
-class RefreshRateConfigs;
} // namespace scheduler
namespace frametimeline {
@@ -62,6 +60,7 @@
namespace surfaceflinger {
+struct LayerCreationArgs;
class NativeWindowSurface;
// The interface that SurfaceFlinger uses to create all of the implementations
@@ -71,7 +70,6 @@
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) = 0;
- virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
bool timestampPropertyValue) = 0;
@@ -90,6 +88,7 @@
virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
+ virtual sp<LayerFE> createLayerFE(const std::string& layerName) = 0;
virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 20fa091..5b73030 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -367,6 +367,14 @@
return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
}
+bool frame_rate_override_for_native_rates(bool defaultValue) {
+ return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue);
+}
+
+bool frame_rate_override_global(bool defaultValue) {
+ return SurfaceFlingerProperties::frame_rate_override_global().value_or(defaultValue);
+}
+
bool enable_layer_caching(bool defaultValue) {
return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 080feee..09629cf 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -96,6 +96,10 @@
bool enable_frame_rate_override(bool defaultValue);
+bool frame_rate_override_for_native_rates(bool defaultValue);
+
+bool frame_rate_override_global(bool defaultValue);
+
bool enable_layer_caching(bool defaultValue);
bool enable_sdr_dimming(bool defaultValue);
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
deleted file mode 100644
index 6797aa6..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright 2016 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#undef LOG_TAG
-#define LOG_TAG "SurfaceInterceptor"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
-#include <fstream>
-
-#include <android-base/file.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-// TODO(marissaw): add new layer state values to SurfaceInterceptor
-
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-namespace impl {
-
-void SurfaceInterceptor::addTransactionTraceListener(
- const sp<gui::ITransactionTraceListener>& listener) {
- sp<IBinder> asBinder = IInterface::asBinder(listener);
-
- std::scoped_lock lock(mListenersMutex);
-
- asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
-
- listener->onToggled(mEnabled); // notifies of current state
-
- mTraceToggledListeners.emplace(asBinder, listener);
-}
-
-void SurfaceInterceptor::binderDied(const wp<IBinder>& who) {
- std::scoped_lock lock(mListenersMutex);
- mTraceToggledListeners.erase(who);
-}
-
-void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
- if (mEnabled) {
- return;
- }
- ATRACE_CALL();
- {
- std::scoped_lock lock(mListenersMutex);
- for (const auto& [_, listener] : mTraceToggledListeners) {
- listener->onToggled(true);
- }
- }
- mEnabled = true;
- std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
- saveExistingDisplaysLocked(displays);
- saveExistingSurfacesLocked(layers);
-}
-
-void SurfaceInterceptor::disable() {
- if (!mEnabled) {
- return;
- }
- ATRACE_CALL();
- {
- std::scoped_lock lock(mListenersMutex);
- for (const auto& [_, listener] : mTraceToggledListeners) {
- listener->onToggled(false);
- }
- }
- mEnabled = false;
- std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
- status_t err(writeProtoFileLocked());
- ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
- ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
- mTrace.Clear();
-}
-
-bool SurfaceInterceptor::isEnabled() {
- return mEnabled;
-}
-
-void SurfaceInterceptor::saveExistingDisplaysLocked(
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
- // Caveat: The initial snapshot does not capture the power mode of the existing displays
- ATRACE_CALL();
- for (size_t i = 0 ; i < displays.size() ; i++) {
- addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
- addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
- }
-}
-
-void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
- ATRACE_CALL();
- for (const auto& l : layers) {
- l->traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* layer) {
- addSurfaceCreationLocked(createTraceIncrementLocked(), sp<Layer>::fromExisting(layer));
- addInitialSurfaceStateLocked(createTraceIncrementLocked(),
- sp<Layer>::fromExisting(layer));
- });
- }
-}
-
-void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
- const sp<const Layer>& layer)
-{
- Transaction* transaction(increment->mutable_transaction());
- const uint32_t layerFlags = layer->getTransactionFlags();
- transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous);
- transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
-
- const int32_t layerId(getLayerId(layer));
- addPositionLocked(transaction, layerId, layer->mDrawingState.transform.tx(),
- layer->mDrawingState.transform.ty());
- addDepthLocked(transaction, layerId, layer->mDrawingState.z);
- addAlphaLocked(transaction, layerId, layer->mDrawingState.color.a);
- addLayerStackLocked(transaction, layerId, layer->mDrawingState.layerStack);
- addCropLocked(transaction, layerId, layer->mDrawingState.crop);
- addCornerRadiusLocked(transaction, layerId, layer->mDrawingState.cornerRadius);
- addBackgroundBlurRadiusLocked(transaction, layerId, layer->mDrawingState.backgroundBlurRadius);
- addBlurRegionsLocked(transaction, layerId, layer->mDrawingState.blurRegions);
- addFlagsLocked(transaction, layerId, layer->mDrawingState.flags,
- layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
- layer_state_t::eLayerSecure);
- addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mDrawingParent));
- addRelativeParentLocked(transaction, layerId,
- getLayerIdFromWeakRef(layer->mDrawingState.zOrderRelativeOf),
- layer->mDrawingState.z);
- addShadowRadiusLocked(transaction, layerId, layer->mDrawingState.shadowRadius);
- addTrustedOverlayLocked(transaction, layerId, layer->mDrawingState.isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
- const DisplayDeviceState& display)
-{
- Transaction* transaction(increment->mutable_transaction());
- transaction->set_synchronous(false);
- transaction->set_animation(false);
-
- addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
- addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
- addDisplayFlagsLocked(transaction, display.sequenceId, display.flags);
- addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
- addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
- display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
-}
-
-status_t SurfaceInterceptor::writeProtoFileLocked() {
- ATRACE_CALL();
- std::string output;
-
- if (!mTrace.IsInitialized()) {
- return NOT_ENOUGH_DATA;
- }
- if (!mTrace.SerializeToString(&output)) {
- return PERMISSION_DENIED;
- }
- if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
- return PERMISSION_DENIED;
- }
-
- return NO_ERROR;
-}
-
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
- sp<IBinder> handle = weakHandle.promote();
- return Layer::fromHandle(handle).promote();
-}
-
-int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
- return layer->sequence;
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const {
- if (layer == nullptr) {
- return -1;
- }
- auto strongLayer = layer.promote();
- return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
- if (handle == nullptr) {
- return -1;
- }
- const sp<const Layer> layer = Layer::fromHandle(handle).promote();
- return layer == nullptr ? -1 : getLayerId(layer);
-}
-
-Increment* SurfaceInterceptor::createTraceIncrementLocked() {
- Increment* increment(mTrace.add_increment());
- increment->set_time_stamp(elapsedRealtimeNano());
- return increment;
-}
-
-SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
- int32_t layerId)
-{
- SurfaceChange* change(transaction->add_surface_change());
- change->set_id(layerId);
- return change;
-}
-
-DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
- int32_t sequenceId)
-{
- DisplayChange* dispChange(transaction->add_display_change());
- dispChange->set_id(sequenceId);
- return dispChange;
-}
-
-void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
- protoRect->set_left(rect.left);
- protoRect->set_top(rect.top);
- protoRect->set_right(rect.right);
- protoRect->set_bottom(rect.bottom);
-}
-
-void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
- int32_t uid) {
- Origin* origin(transaction->mutable_origin());
- origin->set_pid(pid);
- origin->set_uid(uid);
-}
-
-void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
- float x, float y)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- PositionChange* posChange(change->mutable_position());
- posChange->set_x(x);
- posChange->set_y(y);
-}
-
-void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
- uint32_t z)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- LayerChange* depthChange(change->mutable_layer());
- depthChange->set_layer(z);
-}
-
-void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
- uint32_t h)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- SizeChange* sizeChange(change->mutable_size());
- sizeChange->set_w(w);
- sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
- float alpha)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- AlphaChange* alphaChange(change->mutable_alpha());
- alphaChange->set_alpha(alpha);
-}
-
-void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
- const layer_state_t::matrix22_t& matrix)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- MatrixChange* matrixChange(change->mutable_matrix());
- matrixChange->set_dsdx(matrix.dsdx);
- matrixChange->set_dtdx(matrix.dtdx);
- matrixChange->set_dsdy(matrix.dsdy);
- matrixChange->set_dtdy(matrix.dtdy);
-}
-
-void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
- int32_t layerId, const Region& transRegion)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
-
- for (const auto& rect : transRegion) {
- Rectangle* protoRect(transparentChange->add_region());
- setProtoRectLocked(protoRect, rect);
- }
-}
-
-void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags,
- uint8_t mask) {
- // There can be multiple flags changed
- if (mask & layer_state_t::eLayerHidden) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- HiddenFlagChange* flagChange(change->mutable_hidden_flag());
- flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden);
- }
- if (mask & layer_state_t::eLayerOpaque) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
- flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque);
- }
- if (mask & layer_state_t::eLayerSecure) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- SecureFlagChange* flagChange(change->mutable_secure_flag());
- flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure);
- }
-}
-
-void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
- ui::LayerStack layerStack) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- LayerStackChange* layerStackChange(change->mutable_layer_stack());
- layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
- const Rect& rect)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- CropChange* cropChange(change->mutable_crop());
- Rectangle* protoRect(cropChange->mutable_rectangle());
- setProtoRectLocked(protoRect, rect);
-}
-
-void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
- float cornerRadius)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
- cornerRadiusChange->set_corner_radius(cornerRadius);
-}
-
-void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
- int32_t backgroundBlurRadius) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
- blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
-}
-
-void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
- const std::vector<BlurRegion>& blurRegions) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
- for (const auto blurRegion : blurRegions) {
- const auto blurRegionChange = blurRegionsChange->add_blur_regions();
- blurRegionChange->set_blur_radius(blurRegion.blurRadius);
- blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
- blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
- blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
- blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
- blurRegionChange->set_alpha(blurRegion.alpha);
- blurRegionChange->set_left(blurRegion.left);
- blurRegionChange->set_top(blurRegion.top);
- blurRegionChange->set_right(blurRegion.right);
- blurRegionChange->set_bottom(blurRegion.bottom);
- }
-}
-
-void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
- int32_t parentId) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- ReparentChange* overrideChange(change->mutable_reparent());
- overrideChange->set_parent_id(parentId);
-}
-
-void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
- int32_t parentId, int z) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- RelativeParentChange* overrideChange(change->mutable_relative_parent());
- overrideChange->set_relative_parent_id(parentId);
- overrideChange->set_z(z);
-}
-
-void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
- float shadowRadius) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
- overrideChange->set_radius(shadowRadius);
-}
-
-void SurfaceInterceptor::addTrustedOverlayLocked(Transaction* transaction, int32_t layerId,
- bool isTrustedOverlay) {
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- TrustedOverlayChange* overrideChange(change->mutable_trusted_overlay());
- overrideChange->set_is_trusted_overlay(isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
- const layer_state_t& state)
-{
- const sp<const Layer> layer(getLayer(state.surface));
- if (layer == nullptr) {
- ALOGE("An existing layer could not be retrieved with the surface "
- "from the layer_state_t surface in the update transaction");
- return;
- }
-
- const int32_t layerId(getLayerId(layer));
-
- if (state.what & layer_state_t::ePositionChanged) {
- addPositionLocked(transaction, layerId, state.x, state.y);
- }
- if (state.what & layer_state_t::eLayerChanged) {
- addDepthLocked(transaction, layerId, state.z);
- }
- if (state.what & layer_state_t::eAlphaChanged) {
- addAlphaLocked(transaction, layerId, state.alpha);
- }
- if (state.what & layer_state_t::eMatrixChanged) {
- addMatrixLocked(transaction, layerId, state.matrix);
- }
- if (state.what & layer_state_t::eTransparentRegionChanged) {
- addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
- }
- if (state.what & layer_state_t::eFlagsChanged) {
- addFlagsLocked(transaction, layerId, state.flags, state.mask);
- }
- if (state.what & layer_state_t::eLayerStackChanged) {
- addLayerStackLocked(transaction, layerId, state.layerStack);
- }
- if (state.what & layer_state_t::eCropChanged) {
- addCropLocked(transaction, layerId, state.crop);
- }
- if (state.what & layer_state_t::eCornerRadiusChanged) {
- addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
- }
- if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
- addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
- }
- if (state.what & layer_state_t::eBlurRegionsChanged) {
- addBlurRegionsLocked(transaction, layerId, state.blurRegions);
- }
- if (state.what & layer_state_t::eReparent) {
- auto parentHandle = (state.parentSurfaceControlForChild)
- ? state.parentSurfaceControlForChild->getHandle()
- : nullptr;
- addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
- }
- if (state.what & layer_state_t::eRelativeLayerChanged) {
- addRelativeParentLocked(transaction, layerId,
- getLayerIdFromHandle(
- state.relativeLayerSurfaceControl->getHandle()),
- state.z);
- }
- if (state.what & layer_state_t::eShadowRadiusChanged) {
- addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
- }
- if (state.what & layer_state_t::eTrustedOverlayChanged) {
- addTrustedOverlayLocked(transaction, layerId, state.isTrustedOverlay);
- }
- if (state.what & layer_state_t::eStretchChanged) {
- ALOGW("SurfaceInterceptor not implemented for eStretchChanged");
- }
-}
-
-void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
- const DisplayState& state, int32_t sequenceId)
-{
- if (state.what & DisplayState::eSurfaceChanged) {
- addDisplaySurfaceLocked(transaction, sequenceId, state.surface);
- }
- if (state.what & DisplayState::eLayerStackChanged) {
- addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
- }
- if (state.what & DisplayState::eFlagsChanged) {
- addDisplayFlagsLocked(transaction, sequenceId, state.flags);
- }
- if (state.what & DisplayState::eDisplaySizeChanged) {
- addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
- }
- if (state.what & DisplayState::eDisplayProjectionChanged) {
- addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
- state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
- }
-}
-
-void SurfaceInterceptor::addTransactionLocked(
- Increment* increment, const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid,
- int originUid, uint64_t transactionId) {
- Transaction* transaction(increment->mutable_transaction());
- transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
- transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
- setTransactionOriginLocked(transaction, originPid, originUid);
- transaction->set_id(transactionId);
- for (const auto& compState: stateUpdates) {
- addSurfaceChangesLocked(transaction, compState.state);
- }
- for (const auto& disp: changedDisplays) {
- ssize_t dpyIdx = displays.indexOfKey(disp.token);
- if (dpyIdx >= 0) {
- const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
- addDisplayChangesLocked(transaction, disp, dispState.sequenceId);
- }
- }
-}
-
-void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
- const sp<const Layer>& layer)
-{
- SurfaceCreation* creation(increment->mutable_surface_creation());
- creation->set_id(getLayerId(layer));
- creation->set_name(layer->getName());
-}
-
-void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
- const sp<const Layer>& layer)
-{
- SurfaceDeletion* deletion(increment->mutable_surface_deletion());
- deletion->set_id(getLayerId(layer));
-}
-
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
- uint32_t width, uint32_t height, uint64_t frameNumber)
-{
- BufferUpdate* update(increment->mutable_buffer_update());
- update->set_id(layerId);
- update->set_w(width);
- update->set_h(height);
- update->set_frame_number(frameNumber);
-}
-
-void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
- VSyncEvent* event(increment->mutable_vsync_event());
- event->set_when(timestamp);
-}
-
-void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
- const sp<const IGraphicBufferProducer>& surface)
-{
- if (surface == nullptr) {
- return;
- }
- uint64_t bufferQueueId = 0;
- status_t err(surface->getUniqueId(&bufferQueueId));
- if (err == NO_ERROR) {
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
- DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
- surfaceChange->set_buffer_queue_id(bufferQueueId);
- surfaceChange->set_buffer_queue_name(surface->getConsumerName().string());
- }
- else {
- ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
- strerror(-err));
- }
-}
-
-void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
- ui::LayerStack layerStack) {
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
- LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
- layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
- uint32_t flags) {
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
- DisplayFlagsChange* flagsChange(dispChange->mutable_flags());
- flagsChange->set_flags(flags);
-}
-
-void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
- uint32_t w, uint32_t h)
-{
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
- SizeChange* sizeChange(dispChange->mutable_size());
- sizeChange->set_w(w);
- sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
- int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame)
-{
- DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
- ProjectionChange* projectionChange(dispChange->mutable_projection());
- projectionChange->set_orientation(orientation);
- Rectangle* viewportRect(projectionChange->mutable_viewport());
- setProtoRectLocked(viewportRect, viewport);
- Rectangle* frameRect(projectionChange->mutable_frame());
- setProtoRectLocked(frameRect, frame);
-}
-
-void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
- const DisplayDeviceState& info)
-{
- DisplayCreation* creation(increment->mutable_display_creation());
- creation->set_id(info.sequenceId);
- creation->set_name(info.displayName);
- creation->set_is_secure(info.isSecure);
- if (info.physical) {
- creation->set_display_id(info.physical->id.value);
- }
-}
-
-void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) {
- DisplayDeletion* deletion(increment->mutable_display_deletion());
- deletion->set_id(sequenceId);
-}
-
-void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId,
- int32_t mode)
-{
- PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
- powerModeUpdate->set_id(sequenceId);
- powerModeUpdate->set_mode(mode);
-}
-
-void SurfaceInterceptor::saveTransaction(
- const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid,
- uint64_t transactionId) {
- if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
- flags, originPid, originUid, transactionId);
-}
-
-void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
- if (!mEnabled || layer == nullptr) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
-}
-
-void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
- if (!mEnabled || layer == nullptr) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
-}
-
-/**
- * Here we pass the layer by ID instead of by sp<> since this is called without
- * holding the state-lock from a Binder thread. If we required the caller
- * to pass 'this' by sp<> the temporary sp<> constructed could end up
- * being the last reference and we might accidentally destroy the Layer
- * from this binder thread.
- */
-void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
- uint32_t height, uint64_t frameNumber)
-{
- if (!mEnabled) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
-}
-
-void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
- if (!mEnabled) {
- return;
- }
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
-}
-
-void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
- if (!mEnabled) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addDisplayCreationLocked(createTraceIncrementLocked(), info);
-}
-
-void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) {
- if (!mEnabled) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId);
-}
-
-void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) {
- if (!mEnabled) {
- return;
- }
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode);
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
deleted file mode 100644
index 970c3e5..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SURFACEINTERCEPTOR_H
-#define ANDROID_SURFACEINTERCEPTOR_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <mutex>
-
-#include <binder/IBinder.h>
-
-#include <gui/LayerState.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/SortedVector.h>
-#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
-
-#include "DisplayDevice.h"
-
-namespace android {
-
-class BufferItem;
-class Layer;
-class SurfaceFlinger;
-struct ComposerState;
-struct DisplayDeviceState;
-struct DisplayState;
-struct layer_state_t;
-using Transaction = surfaceflinger::Transaction;
-using Trace = surfaceflinger::Trace;
-using Rectangle = surfaceflinger::Rectangle;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Increment = surfaceflinger::Increment;
-using DisplayChange = surfaceflinger::DisplayChange;
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-class SurfaceInterceptor : public IBinder::DeathRecipient {
-public:
- virtual ~SurfaceInterceptor();
-
- // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
- virtual void enable(const SortedVector<sp<Layer>>& layers,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) = 0;
- virtual void disable() = 0;
- virtual bool isEnabled() = 0;
-
- virtual void addTransactionTraceListener(
- const sp<gui::ITransactionTraceListener>& listener) = 0;
- virtual void binderDied(const wp<IBinder>& who) = 0;
-
- // Intercept display and surface transactions
- virtual void saveTransaction(
- const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
- int originUid, uint64_t transactionId) = 0;
-
- // Intercept surface data
- virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
- virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
- virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
- uint64_t frameNumber) = 0;
-
- // Intercept display data
- virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
- virtual void saveDisplayDeletion(int32_t sequenceId) = 0;
- virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0;
- virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
-};
-
-namespace impl {
-
-/*
- * SurfaceInterceptor intercepts and stores incoming streams of window
- * properties on SurfaceFlinger.
- */
-class SurfaceInterceptor final : public android::SurfaceInterceptor {
-public:
- SurfaceInterceptor() = default;
- ~SurfaceInterceptor() override = default;
-
- // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
- void enable(const SortedVector<sp<Layer>>& layers,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) override;
- void disable() override;
- bool isEnabled() override;
-
- void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override;
- void binderDied(const wp<IBinder>& who) override;
-
- // Intercept display and surface transactions
- void saveTransaction(const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
- int originUid, uint64_t transactionId) override;
-
- // Intercept surface data
- void saveSurfaceCreation(const sp<const Layer>& layer) override;
- void saveSurfaceDeletion(const sp<const Layer>& layer) override;
- void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
- uint64_t frameNumber) override;
-
- // Intercept display data
- void saveDisplayCreation(const DisplayDeviceState& info) override;
- void saveDisplayDeletion(int32_t sequenceId) override;
- void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override;
- void saveVSyncEvent(nsecs_t timestamp) override;
-
-private:
- // The creation increments of Surfaces and Displays do not contain enough information to capture
- // the initial state of each object, so a transaction with all of the missing properties is
- // performed at the initial snapshot for each display and surface.
- void saveExistingDisplaysLocked(
- const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
- void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
- void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
- void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
-
- status_t writeProtoFileLocked();
- const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
- int32_t getLayerId(const sp<const Layer>& layer) const;
- int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
- int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
-
- Increment* createTraceIncrementLocked();
- void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
- void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
- void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
- uint32_t height, uint64_t frameNumber);
- void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
- void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
- void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId);
- void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode);
-
- // Add surface transactions to the trace
- SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
- void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
- void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
- void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
- void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
- void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
- void addMatrixLocked(Transaction* transaction, int32_t layerId,
- const layer_state_t::matrix22_t& matrix);
- void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
- const Region& transRegion);
- void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
- void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack);
- void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
- void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
- void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
- int32_t backgroundBlurRadius);
- void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
- const std::vector<BlurRegion>& effectRegions);
- void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
- void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays,
- uint32_t transactionFlags, int originPid, int originUid,
- uint64_t transactionId);
- void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
- void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
- int z);
- void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
- void addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, bool isTrustedOverlay);
-
- // Add display transactions to the trace
- DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
- void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
- const sp<const IGraphicBufferProducer>& surface);
- void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack);
- void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
- void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
- uint32_t h);
- void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
- int32_t orientation, const Rect& viewport, const Rect& frame);
- void addDisplayChangesLocked(Transaction* transaction,
- const DisplayState& state, int32_t sequenceId);
-
- // Add transaction origin to trace
- void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
-
- bool mEnabled {false};
- std::string mOutputFileName {DEFAULT_FILENAME};
- std::mutex mTraceMutex {};
- Trace mTrace {};
- std::mutex mListenersMutex;
- std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
- GUARDED_BY(mListenersMutex);
-};
-
-} // namespace impl
-
-} // namespace android
-
-#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c8eef46..630cef1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -69,6 +69,8 @@
return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
case GameMode::Battery:
return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
+ case GameMode::Custom:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM;
default:
return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
}
@@ -89,7 +91,7 @@
}
} // namespace
-bool TimeStats::populateGlobalAtom(std::string* pulledData) {
+bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStartLegacy == 0) {
@@ -137,10 +139,11 @@
// Always clear data.
clearGlobalLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
-bool TimeStats::populateLayerAtom(std::string* pulledData) {
+bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -234,7 +237,8 @@
// Always clear data.
clearLayersLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
@@ -250,7 +254,7 @@
}
}
-bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) {
bool success = false;
if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
success = populateGlobalAtom(pulledData);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 09c3217..5f58657 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -47,7 +47,7 @@
virtual ~TimeStats() = default;
// Process a pull request from statsd.
- virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0;
+ virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
@@ -245,7 +245,7 @@
TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
- bool onPullAtom(const int atomId, std::string* pulledData) override;
+ bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -293,8 +293,8 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- bool populateGlobalAtom(std::string* pulledData);
- bool populateLayerAtom(std::string* pulledData);
+ bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
+ bool populateLayerAtom(std::vector<uint8_t>* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate, SetFrameRateVote,
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index a46ecd1..8615947 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -173,6 +173,7 @@
GAME_MODE_STANDARD = 2;
GAME_MODE_PERFORMANCE = 3;
GAME_MODE_BATTERY = 4;
+ GAME_MODE_CUSTOM = 5;
}
// Game mode that the layer was running at. Used to track user engagement
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index e73dac6..b32001c 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -55,6 +55,7 @@
TRACE_EXTRA = 1 << 3,
TRACE_HWC = 1 << 4,
TRACE_BUFFERS = 1 << 5,
+ TRACE_VIRTUAL_DISPLAYS = 1 << 6,
TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
};
void setTraceFlags(uint32_t flags);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index dcc529e..2f46487 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -111,7 +111,7 @@
}
if (layer.what & layer_state_t::eAlphaChanged) {
- proto.set_alpha(layer.alpha);
+ proto.set_alpha(layer.color.a);
}
if (layer.what & layer_state_t::eColorChanged) {
@@ -123,8 +123,8 @@
if (layer.what & layer_state_t::eTransparentRegionChanged) {
LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
}
- if (layer.what & layer_state_t::eTransformChanged) {
- proto.set_transform(layer.transform);
+ if (layer.what & layer_state_t::eBufferTransformChanged) {
+ proto.set_transform(layer.bufferTransform);
}
if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
@@ -310,10 +310,10 @@
int32_t layerCount = proto.layer_changes_size();
t.states.reserve(static_cast<size_t>(layerCount));
for (int i = 0; i < layerCount; i++) {
- ComposerState s;
+ ResolvedComposerState s;
s.state.what = 0;
fromProto(proto.layer_changes(i), s.state);
- t.states.add(s);
+ t.states.emplace_back(s);
}
int32_t displayCount = proto.display_changes_size();
@@ -395,7 +395,7 @@
}
if (proto.what() & layer_state_t::eAlphaChanged) {
- layer.alpha = proto.alpha();
+ layer.color.a = proto.alpha();
}
if (proto.what() & layer_state_t::eColorChanged) {
@@ -407,8 +407,8 @@
if (proto.what() & layer_state_t::eTransparentRegionChanged) {
LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
}
- if (proto.what() & layer_state_t::eTransformChanged) {
- layer.transform = proto.transform();
+ if (proto.what() & layer_state_t::eBufferTransformChanged) {
+ layer.bufferTransform = proto.transform();
}
if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) {
layer.transformToDisplayInverse = proto.transform_to_display_inverse();
@@ -452,7 +452,7 @@
layer.parentSurfaceControlForChild =
sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
- static_cast<int32_t>(layerId));
+ static_cast<int32_t>(layerId), "");
}
}
if (proto.what() & layer_state_t::eRelativeLayerChanged) {
@@ -463,7 +463,7 @@
layer.relativeLayerSurfaceControl =
sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
- static_cast<int32_t>(layerId));
+ static_cast<int32_t>(layerId), "");
}
layer.z = proto.z();
}
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 872a901..2232bb9 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <gui/fake/BufferData.h>
#include <layerproto/TransactionProto.h>
#include <utils/RefBase.h>
@@ -43,35 +44,6 @@
TracingLayerCreationArgs args;
};
-// Class which exposes buffer properties from BufferData without holding on to the actual buffer
-// handle.
-class BufferDataStub : public BufferData {
-public:
- BufferDataStub(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
- uint64_t outUsage)
- : mBufferId(bufferId),
- mWidth(width),
- mHeight(height),
- mPixelFormat(pixelFormat),
- mOutUsage(outUsage) {}
- bool hasBuffer() const override { return mBufferId != 0; }
- bool hasSameBuffer(const BufferData& other) const override {
- return getId() == other.getId() && frameNumber == other.frameNumber;
- }
- uint32_t getWidth() const override { return mWidth; }
- uint32_t getHeight() const override { return mHeight; }
- uint64_t getId() const override { return mBufferId; }
- PixelFormat getPixelFormat() const override { return mPixelFormat; }
- uint64_t getUsage() const override { return mOutUsage; }
-
-private:
- uint64_t mBufferId;
- uint32_t mWidth;
- uint32_t mHeight;
- int32_t mPixelFormat;
- uint64_t mOutUsage;
-};
-
class TransactionProtoParser {
public:
// Utility class to map handles to ids and buffers to buffer properties without pulling
@@ -87,7 +59,7 @@
virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
uint32_t height, int32_t pixelFormat,
uint64_t usage) const {
- return std::make_shared<BufferDataStub>(bufferId, width, height, pixelFormat, usage);
+ return std::make_shared<fake::BufferData>(bufferId, width, height, pixelFormat, usage);
}
virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
uint64_t* /* outBufferId */,
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 8817178..f1a6c0e 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -47,10 +47,6 @@
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- sp<SurfaceInterceptor> createSurfaceInterceptor() override {
- return sp<android::impl::SurfaceInterceptor>::make();
- }
-
sp<StartPropertySetThread> createStartPropertySetThread(
bool /* timestampPropertyValue */) override {
return sp<StartPropertySetThread>();
@@ -86,6 +82,8 @@
sp<Layer> createEffectLayer(const LayerCreationArgs& args) { return sp<Layer>::make(args); }
+ sp<LayerFE> createLayerFE(const std::string& layerName) { return sp<LayerFE>::make(layerName); }
+
std::unique_ptr<FrameTracer> createFrameTracer() override {
return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
}
@@ -102,7 +100,8 @@
MockSurfaceFlinger(Factory& factory)
: SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {}
std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
- const BufferData& bufferData, const char* /* layerName */) const override {
+ BufferData& bufferData, const char* /* layerName */,
+ uint64_t /* transactionId */) override {
return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(),
bufferData.getHeight(),
bufferData.getId(),
@@ -213,11 +212,10 @@
TracingLayerCreationArgs tracingArgs;
parser.fromProto(entry.added_layers(j), tracingArgs);
- sp<IBinder> outHandle;
- int32_t outLayerId;
+ gui::CreateSurfaceResult outResult;
LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name,
- tracingArgs.flags, LayerMetadata());
- args.sequence = std::make_optional<int32_t>(tracingArgs.layerId);
+ tracingArgs.flags, LayerMetadata(),
+ std::make_optional<int32_t>(tracingArgs.layerId));
if (tracingArgs.mirrorFromId == -1) {
sp<IBinder> parentHandle = nullptr;
@@ -228,28 +226,21 @@
} else if (tracingArgs.parentId != -1) {
parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId);
}
- mFlinger.createLayer(args, &outHandle, parentHandle, &outLayerId,
- nullptr /* parentLayer */, nullptr /* outTransformHint */);
+ mFlinger.createLayer(args, parentHandle, outResult);
} else {
sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId);
- mFlinger.mirrorLayer(args, mirrorFromHandle, &outHandle, &outLayerId);
+ mFlinger.mirrorLayer(args, mirrorFromHandle, outResult);
}
- LOG_ALWAYS_FATAL_IF(outLayerId != tracingArgs.layerId,
+ LOG_ALWAYS_FATAL_IF(outResult.layerId != tracingArgs.layerId,
"Could not create layer expected:%d actual:%d", tracingArgs.layerId,
- outLayerId);
- dataMapper->mLayerHandles[tracingArgs.layerId] = outHandle;
+ outResult.layerId);
+ dataMapper->mLayerHandles[tracingArgs.layerId] = outResult.handle;
}
for (int j = 0; j < entry.transactions_size(); j++) {
// apply transactions
TransactionState transaction = parser.fromProto(entry.transactions(j));
- mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime,
- transaction.isAutoTimestamp, {},
- transaction.hasListenerCallbacks,
- transaction.listenerCallbacks, transaction.id);
+ mFlinger.setTransactionStateInternal(transaction);
}
const auto frameTime = TimePoint::fromNs(entry.elapsed_realtime_nanos());
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index f3cf42d..9f9ae48 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -52,6 +52,10 @@
;
ALOGD("Generating %s...", outputLayersTracePath);
std::cout << "Generating " << outputLayersTracePath << "\n";
+
+ // sink any log spam from the stubbed surfaceflinger
+ __android_log_set_logger([](const struct __android_log_message* /* log_message */) {});
+
if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
return -1;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 23ea7a5..c09bcce 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -19,20 +19,34 @@
#include <condition_variable>
#include <deque>
#include <mutex>
+#include <optional>
#include <queue>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <android-base/thread_annotations.h>
+#include <android/gui/ITransactionCompletedListener.h>
+
#include <binder/IBinder.h>
-#include <ftl/future.h>
-#include <gui/ITransactionCompletedListener.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
+#include <renderengine/RenderEngine.h>
#include <ui/Fence.h>
#include <ui/FenceResult.h>
namespace android {
+using gui::CallbackId;
+using gui::FrameEventHistoryStats;
+using gui::IListenerHash;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
+using gui::ListenerCallbacks;
+using gui::ListenerStats;
+using gui::ReleaseCallbackId;
+using gui::TransactionStats;
+
class CallbackHandle : public RefBase {
public:
CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
@@ -48,7 +62,7 @@
std::vector<ftl::SharedFuture<FenceResult>> previousReleaseFences;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
nsecs_t latchTime = -1;
- uint32_t transformHint = 0;
+ std::optional<uint32_t> transformHint = std::nullopt;
uint32_t currentMaxAcquiredBufferCount = 0;
std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
CompositorTiming compositorTiming;
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 61f0fa6..380301f 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -20,19 +20,26 @@
#include <memory>
#include <mutex>
#include <vector>
+#include "renderengine/ExternalTexture.h"
#include <gui/LayerState.h>
#include <system/window.h>
namespace android {
-class CountDownLatch;
+// Extends the client side composer state by resolving buffer.
+class ResolvedComposerState : public ComposerState {
+public:
+ ResolvedComposerState() = default;
+ ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+};
struct TransactionState {
TransactionState() = default;
TransactionState(const FrameTimelineInfo& frameTimelineInfo,
- const Vector<ComposerState>& composerStates,
+ std::vector<ResolvedComposerState>& composerStates,
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -40,7 +47,7 @@
bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
int originPid, int originUid, uint64_t transactionId)
: frameTimelineInfo(frameTimelineInfo),
- states(composerStates),
+ states(std::move(composerStates)),
displays(displayStates),
flags(transactionFlags),
applyToken(applyToken),
@@ -59,9 +66,20 @@
// Invokes `void(const layer_state_t&)` visitor for matching layers.
template <typename Visitor>
void traverseStatesWithBuffers(Visitor&& visitor) const {
- for (const auto& [state] : states) {
- if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) {
- visitor(state);
+ for (const auto& state : states) {
+ if (state.state.hasBufferChanges() && state.state.hasValidBuffer() &&
+ state.state.surface) {
+ visitor(state.state);
+ }
+ }
+ }
+
+ template <typename Visitor>
+ void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) const {
+ for (const auto& state : states) {
+ if (state.state.hasBufferChanges() && state.state.hasValidBuffer() &&
+ state.state.surface) {
+ if (!visitor(state.state)) return;
}
}
}
@@ -72,8 +90,8 @@
bool isFrameActive() const {
if (!displays.empty()) return true;
- for (const auto& [state] : states) {
- if (state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) {
+ for (const auto& state : states) {
+ if (state.state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) {
return true;
}
}
@@ -82,7 +100,7 @@
}
FrameTimelineInfo frameTimelineInfo;
- Vector<ComposerState> states;
+ std::vector<ResolvedComposerState> states;
Vector<DisplayState> displays;
uint32_t flags;
sp<IBinder> applyToken;
@@ -97,49 +115,7 @@
int originPid;
int originUid;
uint64_t id;
- std::shared_ptr<CountDownLatch> transactionCommittedSignal;
bool sentFenceTimeoutWarning = false;
};
-class CountDownLatch {
-public:
- enum {
- eSyncTransaction = 1 << 0,
- };
- explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
-
- // True if there is no waiting condition after count down.
- bool countDown(uint32_t flag) {
- std::unique_lock<std::mutex> lock(mMutex);
- if (mFlags == 0) {
- return true;
- }
- mFlags &= ~flag;
- if (mFlags == 0) {
- mCountDownComplete.notify_all();
- return true;
- }
- return false;
- }
-
- // Return true if triggered.
- bool wait_until(const std::chrono::nanoseconds& timeout) const {
- std::unique_lock<std::mutex> lock(mMutex);
- const auto untilTime = std::chrono::system_clock::now() + timeout;
- while (mFlags != 0) {
- // Conditional variables can be woken up sporadically, so we check count
- // to verify the wakeup was triggered by |countDown|.
- if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
- return false;
- }
- }
- return true;
- }
-
-private:
- uint32_t mFlags;
- mutable std::condition_variable mCountDownComplete;
- mutable std::mutex mMutex;
-};
-
} // namespace android
diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h
new file mode 100644
index 0000000..ee94217
--- /dev/null
+++ b/services/surfaceflinger/Utils/Dumper.h
@@ -0,0 +1,119 @@
+/*
+ * 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 <string>
+#include <string_view>
+
+#include <ftl/optional.h>
+
+namespace android::utils {
+
+// Dumps variables by appending their name and value to the output string. A variable is formatted
+// as "name=value". If the name or value is empty, the format is "value" or "name=", respectively.
+// A value of user-defined type T is stringified via `std::string to_string(const T&)`, which must
+// be defined in the same namespace as T per the rules of ADL (argument-dependent lookup).
+//
+// TODO(b/249828573): Consolidate with <compositionengine/impl/DumpHelpers.h>
+class Dumper {
+public:
+ explicit Dumper(std::string& out) : mOut(out) {}
+
+ void eol() { mOut += '\n'; }
+
+ void dump(std::string_view name, std::string_view value = {}) {
+ using namespace std::string_view_literals;
+
+ for (int i = mIndent; i-- > 0;) mOut += " "sv;
+ mOut += name;
+ if (!name.empty()) mOut += '=';
+ mOut += value;
+ eol();
+ }
+
+ void dump(std::string_view name, const std::string& value) {
+ dump(name, static_cast<const std::string_view&>(value));
+ }
+
+ void dump(std::string_view name, bool value) {
+ using namespace std::string_view_literals;
+ dump(name, value ? "true"sv : "false"sv);
+ }
+
+ template <typename T>
+ void dump(std::string_view name, const std::optional<T>& opt) {
+ if (opt) {
+ dump(name, *opt);
+ } else {
+ using namespace std::string_view_literals;
+ dump(name, "nullopt"sv);
+ }
+ }
+
+ template <typename T>
+ void dump(std::string_view name, const ftl::Optional<T>& opt) {
+ dump(name, static_cast<const std::optional<T>&>(opt));
+ }
+
+ template <typename T, typename... Ts>
+ void dump(std::string_view name, const T& value, const Ts&... rest) {
+ std::string string;
+
+ constexpr bool kIsTuple = sizeof...(Ts) > 0;
+ if constexpr (kIsTuple) {
+ string += '{';
+ }
+
+ using std::to_string;
+ string += to_string(value);
+
+ if constexpr (kIsTuple) {
+ string += ((", " + to_string(rest)) + ...);
+ string += '}';
+ }
+
+ dump(name, string);
+ }
+
+ struct Indent {
+ explicit Indent(Dumper& dumper) : dumper(dumper) { dumper.mIndent++; }
+ ~Indent() { dumper.mIndent--; }
+
+ Dumper& dumper;
+ };
+
+ struct Section {
+ Section(Dumper& dumper, std::string_view heading) : dumper(dumper) {
+ dumper.dump({}, heading);
+ indent.emplace(dumper);
+ }
+
+ ~Section() {
+ indent.reset();
+ dumper.eol();
+ }
+
+ Dumper& dumper;
+ std::optional<Indent> indent;
+ };
+
+private:
+ std::string& mOut;
+ int mIndent = 0;
+};
+
+} // namespace android::utils
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index fae9165..8a6af10 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -116,7 +116,7 @@
class DisplayHardwareFuzzer {
public:
DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
- mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value_or(
+ mPhysicalDisplayId = TestableSurfaceFlinger::getFirstDisplayId().value_or(
PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint8_t>()));
};
void process();
@@ -326,8 +326,8 @@
invokeComposerHal2_3(&composer, display, outLayer);
invokeComposerHal2_4(&composer, display, outLayer);
- composer.executeCommands();
- composer.resetCommands();
+ composer.executeCommands(display);
+ composer.resetCommands(display);
composer.destroyLayer(display, outLayer);
composer.destroyVirtualDisplay(display);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index 79112bd..ce4d18f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -130,7 +130,7 @@
mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>();
mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>();
mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>();
- mFlinger->hasWideColorDisplay = mFdp.ConsumeBool();
+ mTestableFlinger.mutableSupportsWideColor() = mFdp.ConsumeBool();
mFlinger->useContextPriority = mFdp.ConsumeBool();
mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
@@ -150,7 +150,7 @@
sp<IBinder> handle = defaultServiceManager()->checkService(
String16(mFdp.ConsumeRandomLengthString().c_str()));
- mFlinger->fromHandle(handle);
+ LayerHandle::getLayer(handle);
mFlinger->disableExpensiveRendering();
}
@@ -245,6 +245,7 @@
setDisplayStateLocked();
setTransactionState();
+ mTestableFlinger.flushTransactionQueues();
onTransact(data, size);
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 69dbfe0..81ca659 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -34,11 +34,12 @@
#include "DisplayHardware/ComposerHal.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/VSyncTracker.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncController.h"
@@ -46,7 +47,6 @@
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
#include "ThreadContext.h"
#include "TimeStats/TimeStats.h"
@@ -60,7 +60,6 @@
#include "tests/unittests/mock/MockFrameTimeline.h"
#include "tests/unittests/mock/MockFrameTracer.h"
#include "tests/unittests/mock/MockNativeWindowSurface.h"
-#include "tests/unittests/mock/MockSurfaceInterceptor.h"
#include "tests/unittests/mock/MockTimeStats.h"
#include "tests/unittests/mock/MockVSyncTracker.h"
#include "tests/unittests/mock/MockVsyncController.h"
@@ -82,7 +81,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -219,18 +217,21 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs,
- ISchedulerCallback &callback)
+ TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
+ ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<android::mock::VsyncController>(),
- std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs,
+ std::make_unique<android::mock::VSyncTracker>(), selectorPtr,
callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::unique_ptr<VSyncTracker> tracker,
- std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback)
+ std::shared_ptr<RefreshRateSelector> selectorPtr,
+ ISchedulerCallback& callback)
: Scheduler(*this, callback, Feature::kContentDetection) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
- setRefreshRateConfigs(std::move(configs));
+
+ const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
+ registerDisplay(displayId, std::move(selectorPtr));
}
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -242,7 +243,7 @@
auto &mutableLayerHistory() { return mLayerHistory; }
- auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
+ auto refreshRateSelector() { return leaderSelectorPtr(); }
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
@@ -270,7 +271,7 @@
mPolicy.cachedModeChangedParams.reset();
}
- void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) {
return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
@@ -311,15 +312,11 @@
}
std::unique_ptr<scheduler::Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs> &,
- scheduler::ISchedulerCallback &) {
+ const std::shared_ptr<scheduler::RefreshRateSelector>&,
+ scheduler::ISchedulerCallback&) {
return nullptr;
}
- sp<SurfaceInterceptor> createSurfaceInterceptor() override {
- return sp<android::impl::SurfaceInterceptor>::make();
- }
-
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
return sp<StartPropertySetThread>::make(timestampPropertyValue);
}
@@ -360,6 +357,10 @@
return sp<Layer>::make(args);
}
+ sp<LayerFE> createLayerFE(const std::string &layerName) override {
+ return sp<LayerFE>::make(layerName);
+ }
+
std::unique_ptr<FrameTracer> createFrameTracer() override {
return std::make_unique<android::mock::FrameTracer>();
}
@@ -418,7 +419,7 @@
void onPullAtom(FuzzedDataProvider *fdp) {
const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
- std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+ std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>();
bool success = fdp->ConsumeBool();
mFlinger->onPullAtom(atomId, &pulledData, &success);
}
@@ -455,10 +456,6 @@
mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>());
mFlinger->updateColorMatrixLocked();
mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>());
-
- const CountDownLatch transactionCommittedSignal(fdp->ConsumeIntegral<uint32_t>());
- mFlinger->waitForSynchronousTransaction(transactionCommittedSignal);
- mFlinger->signalSynchronousTransactions(fdp->ConsumeIntegral<uint32_t>());
}
void getCompositionPreference() {
@@ -494,14 +491,14 @@
mFlinger->getDisplayState(display, &displayState);
}
- void getStaticDisplayInfo(sp<IBinder> &display) {
+ void getStaticDisplayInfo(int64_t displayId) {
ui::StaticDisplayInfo staticDisplayInfo;
- mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+ mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo);
}
- void getDynamicDisplayInfo(sp<IBinder> &display) {
+ void getDynamicDisplayInfo(int64_t displayId) {
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
- mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+ mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
void getDisplayNativePrimaries(sp<IBinder> &display) {
android::ui::DisplayPrimaries displayPrimaries;
@@ -509,16 +506,8 @@
}
void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
- ui::DisplayModeId outDefaultMode;
- bool outAllowGroupSwitching;
- float outPrimaryRefreshRateMin;
- float outPrimaryRefreshRateMax;
- float outAppRequestRefreshRateMin;
- float outAppRequestRefreshRateMax;
- mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching,
- &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax,
- &outAppRequestRefreshRateMin,
- &outAppRequestRefreshRateMax);
+ gui::DisplayModeSpecs _;
+ mFlinger->getDesiredDisplayModeSpecs(display, &_);
}
void setVsyncConfig(FuzzedDataProvider *fdp) {
@@ -526,7 +515,14 @@
mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
}
- sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+ // TODO(b/248317436): extend to cover all displays for multi-display devices
+ static std::optional<PhysicalDisplayId> getFirstDisplayId() {
+ std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) return {};
+ return ids.front();
+ }
+
+ std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -537,9 +533,8 @@
ui::PixelFormat pixelFormat{};
mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat);
- PhysicalDisplayId physicalDisplayId =
- SurfaceComposerClient::getInternalDisplayId().value_or(
- PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
+ PhysicalDisplayId physicalDisplayId = getFirstDisplayId().value_or(
+ PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId);
sp<IBinder> display =
@@ -554,13 +549,13 @@
mFlinger->bootFinished();
- return display;
+ return {display, physicalDisplayId.value};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- sp<IBinder> display = fuzzBoot(&mFdp);
+ auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -568,8 +563,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(display);
- getDynamicDisplayInfo(display);
+ getStaticDisplayInfo(displayId);
+ getDynamicDisplayInfo(displayId);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
@@ -580,8 +575,6 @@
onPullAtom(&mFdp);
- mFlinger->injectVSync(mFdp.ConsumeIntegral<nsecs_t>());
-
getCompositionPreference();
getDisplayedContentSample(display, &mFdp);
getDesiredDisplayModeSpecs(display);
@@ -634,7 +627,8 @@
}
void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
- mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+ mFlinger->mRenderEngine = std::move(renderEngine);
+ mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
}
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
@@ -661,9 +655,8 @@
modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
}
- mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
- const auto fps =
- FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateConfigs->getActiveMode().getFps());
+ mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
+ const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -672,7 +665,7 @@
hal::PowerMode::OFF);
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
- std::move(vsyncTracker), mRefreshRateConfigs,
+ std::move(vsyncTracker), mRefreshRateSelector,
*(callback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
@@ -732,21 +725,30 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto &getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
- auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+ auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+ auto &getPendingTransactionQueue() {
+ return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+ }
- auto setTransactionState(
- const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states,
- const Vector<DisplayState> &displays, uint32_t flags, const sp<IBinder> &applyToken,
- const InputWindowCommands &inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> &listenerCallbacks, uint64_t transactionId) {
+ auto setTransactionState(const FrameTimelineInfo &frameTimelineInfo,
+ Vector<ComposerState> &states, const Vector<DisplayState> &displays,
+ uint32_t flags, const sp<IBinder> &applyToken,
+ const InputWindowCommands &inputWindowCommands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks> &listenerCallbacks,
+ uint64_t transactionId) {
return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
inputWindowCommands, desiredPresentTime,
isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
listenerCallbacks, transactionId);
}
+ auto flushTransactionQueues() {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return mFlinger->flushTransactionQueues(VsyncId{0});
+ }
+
auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
return mFlinger->onTransact(code, data, reply, flags);
}
@@ -761,28 +763,26 @@
/* Read-write access to private data to set up preconditions and assert
* post-conditions.
*/
+ auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
+ auto& mutableCurrentState() { return mFlinger->mCurrentState; }
+ auto& mutableDisplays() { return mFlinger->mDisplays; }
+ auto& mutableDrawingState() { return mFlinger->mDrawingState; }
- auto &mutableCurrentState() { return mFlinger->mCurrentState; }
- auto &mutableDisplays() { return mFlinger->mDisplays; }
- auto &mutableDrawingState() { return mFlinger->mDrawingState; }
- auto &mutableInterceptor() { return mFlinger->mInterceptor; }
-
- auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); }
+ auto fromHandle(const sp<IBinder> &handle) { return LayerHandle::getLayer(handle); }
~TestableSurfaceFlinger() {
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
- mutableInterceptor().clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
- mFlinger->mCompositionEngine->setRenderEngine(
- std::unique_ptr<renderengine::RenderEngine>());
+ mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
+ mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
}
private:
void setVsyncEnabled(bool) override {}
- void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+ void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
@@ -790,7 +790,7 @@
sp<SurfaceFlinger> mFlinger =
sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
scheduler::TestableScheduler *mScheduler = nullptr;
- std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+ std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 0a142c3..acfc1d4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -146,8 +146,7 @@
layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
layer->fenceHasSignaled();
- layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>(),
- false /*updatingOutputGeometryThisFrame*/);
+ layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
const std::vector<sp<CallbackHandle>> callbacks;
layer->setTransactionCompletedListeners(callbacks);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 66bac44..7959e52 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -23,6 +23,7 @@
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/OneShotTimer.h"
+#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/VSyncDispatchTimerQueue.h"
#include "Scheduler/VSyncPredictor.h"
#include "Scheduler/VSyncReactor.h"
@@ -38,7 +39,7 @@
(72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(),
(120_Hz).getPeriodNsecs()};
-constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateConfigs::LayerVoteType>();
+constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
@@ -59,7 +60,7 @@
private:
void fuzzRefreshRateSelection();
- void fuzzRefreshRateConfigs();
+ void fuzzRefreshRateSelector();
void fuzzPresentLatencyTracker();
void fuzzVSyncModulator();
void fuzzVSyncPredictor();
@@ -92,7 +93,7 @@
const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr,
- nullptr, nullptr, getVsyncPeriod);
+ nullptr, getVsyncPeriod);
thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
sp<EventThreadConnection> connection =
@@ -238,8 +239,8 @@
time1 += mFdp.PickValueInArray(kVsyncPeriods);
time2 += mFdp.PickValueInArray(kVsyncPeriods);
}
- historyV1.summarize(*scheduler->refreshRateConfigs(), time1);
- historyV1.summarize(*scheduler->refreshRateConfigs(), time2);
+ historyV1.summarize(*scheduler->refreshRateSelector(), time1);
+ historyV1.summarize(*scheduler->refreshRateSelector(), time2);
scheduler->createConnection(std::make_unique<android::mock::EventThread>());
@@ -248,7 +249,9 @@
scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
- dump<scheduler::TestableScheduler>(scheduler, &mFdp);
+ std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength);
+ utils::Dumper dumper(result);
+ scheduler->dump(dumper);
}
void SchedulerFuzzer::fuzzVSyncReactor() {
@@ -327,9 +330,9 @@
layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
}
-void SchedulerFuzzer::fuzzRefreshRateConfigs() {
- using RefreshRateConfigs = scheduler::RefreshRateConfigs;
- using LayerRequirement = RefreshRateConfigs::LayerRequirement;
+void SchedulerFuzzer::fuzzRefreshRateSelector() {
+ using RefreshRateSelector = scheduler::RefreshRateSelector;
+ using LayerRequirement = RefreshRateSelector::LayerRequirement;
using RefreshRateStats = scheduler::RefreshRateStats;
const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
@@ -345,35 +348,49 @@
Fps::fromValue(static_cast<float>(fps))));
}
- RefreshRateConfigs refreshRateConfigs(displayModes, modeId);
+ RefreshRateSelector refreshRateSelector(displayModes, modeId);
- const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
- refreshRateConfigs.getRankedRefreshRates(layers, globalSignals);
+ refreshRateSelector.getRankedFrameRates(layers, globalSignals);
layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>());
layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values);
auto frameRateOverrides =
- refreshRateConfigs.getFrameRateOverrides(layers,
- Fps::fromValue(
+ refreshRateSelector.getFrameRateOverrides(layers,
+ Fps::fromValue(
+ mFdp.ConsumeFloatingPoint<float>()),
+ globalSignals);
+
+ {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ refreshRateSelector.setPolicy(
+ RefreshRateSelector::
+ DisplayManagerPolicy{modeId,
+ {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+ Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
+ refreshRateSelector.setPolicy(
+ RefreshRateSelector::OverridePolicy{modeId,
+ {Fps::fromValue(
mFdp.ConsumeFloatingPoint<float>()),
- globalSignals);
+ Fps::fromValue(
+ mFdp.ConsumeFloatingPoint<float>())}});
+ refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
- refreshRateConfigs.setDisplayManagerPolicy(
- {modeId,
- {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
- Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
- FTL_FAKE_GUARD(kMainThreadContext, refreshRateConfigs.setActiveModeId(modeId));
+ refreshRateSelector.setActiveMode(modeId,
+ Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
+ }
- RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
- mFdp.ConsumeFloatingPoint<float>()),
- Fps::fromValue(
- mFdp.ConsumeFloatingPoint<float>()));
- RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
- Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
+ RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
+ mFdp.ConsumeFloatingPoint<float>()),
+ Fps::fromValue(
+ mFdp.ConsumeFloatingPoint<float>()));
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+ Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
android::mock::TimeStats timeStats;
RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
@@ -394,7 +411,7 @@
void SchedulerFuzzer::process() {
fuzzRefreshRateSelection();
- fuzzRefreshRateConfigs();
+ fuzzRefreshRateSelector();
fuzzPresentLatencyTracker();
fuzzVSyncModulator();
fuzzVSyncPredictor();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 1a49ead..2bc5b46 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -27,7 +27,6 @@
#include "Clock.h"
#include "Layer.h"
#include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VSyncTracker.h"
#include "Scheduler/VsyncModulator.h"
@@ -117,6 +116,8 @@
return true;
}
+ void setDivisor(unsigned) override {}
+
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
return timePoint;
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index b687abc..4c6a9cf 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -98,8 +98,7 @@
eReparent = 0x00008000;
eColorChanged = 0x00010000;
- eDestroySurface = 0x00020000;
- eTransformChanged = 0x00040000;
+ eBufferTransformChanged = 0x00040000;
eTransformToDisplayInverseChanged = 0x00080000;
eCropChanged = 0x00100000;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index bcbe21a..8540c3d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -445,6 +445,28 @@
prop_name: "ro.surface_flinger.enable_frame_rate_override"
}
+# Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate
+# to native display refresh rates only. Before introducing this flag, native display refresh rates
+# was the default behaviour. With this flag we can control which behaviour we want explicitly.
+# This flag is introduced as a fail-safe mechanism and planned to be defaulted to false.
+prop {
+ api_name: "frame_rate_override_for_native_rates"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
+}
+
+# Enables the frame rate override feature (enable_frame_rate_override) to
+# override the frame rate globally instead of only for individual apps.
+prop {
+ api_name: "frame_rate_override_global"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.frame_rate_override_global"
+}
+
# Enables Layer Caching
prop {
api_name: "enable_layer_caching"
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 348a462..9338133 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -61,6 +61,14 @@
prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays"
}
prop {
+ api_name: "frame_rate_override_for_native_rates"
+ prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
+ }
+ prop {
+ api_name: "frame_rate_override_global"
+ prop_name: "ro.surface_flinger.frame_rate_override_global"
+ }
+ prop {
api_name: "has_HDR_display"
prop_name: "ro.surface_flinger.has_HDR_display"
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 13ce65d..bd6367d 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -56,13 +56,11 @@
"SetFrameRateOverride_test.cpp",
"SetGeometry_test.cpp",
"Stress_test.cpp",
- "SurfaceInterceptor_test.cpp",
"VirtualDisplay_test.cpp",
"WindowInfosListener_test.cpp",
],
data: ["SurfaceFlinger_test.filter"],
static_libs: [
- "libtrace_proto",
"liblayers_proto",
"android.hardware.graphics.composer@2.1",
],
@@ -128,7 +126,6 @@
}
subdirs = [
- "fakehwc",
"hwc2",
"unittests",
"utils",
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index 432e227..f2874ae 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -30,7 +30,10 @@
TEST(BootDisplayModeTest, setBootDisplayMode) {
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
bool bootModeSupport = false;
binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
@@ -42,7 +45,9 @@
TEST(BootDisplayModeTest, clearBootDisplayMode) {
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
bool bootModeSupport = false;
binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 775de4a..4a45eb5 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -74,8 +74,26 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
}
+ static sp<IBinder> getFirstDisplayToken() {
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ return nullptr;
+ }
+
+ return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ }
+
+ static std::optional<uint64_t> getFirstDisplayId() {
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ return std::nullopt;
+ }
+
+ return ids.front().value;
+ }
+
void setupBackgroundSurface() {
- mDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ mDisplay = getFirstDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
ui::DisplayMode mode;
@@ -158,39 +176,33 @@
}
TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
- std::function<bool()> condition = [] {
- return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
- };
+ std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
// Anyone can access display information.
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
// The following methods are tested with a UID that is not root, graphics,
// or system, to show that anyone can access them.
UIDFaker f(AID_BIN);
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
- ASSERT_TRUE(display != nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
- Vector<ui::DisplayMode> modes;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info));
}
TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
std::function<status_t()> condition = [=]() {
ui::DynamicDisplayInfo info;
- return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto display = getFirstDisplayToken();
std::function<status_t()> condition = [=]() {
ui::DisplayPrimaries primaries;
return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
@@ -199,30 +211,19 @@
}
TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
- ui::DisplayModeId defaultMode;
- bool allowGroupSwitching;
- float primaryFpsMin;
- float primaryFpsMax;
- float appRequestFpsMin;
- float appRequestFpsMax;
- status_t res =
- SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode,
- &allowGroupSwitching, &primaryFpsMin,
- &primaryFpsMax, &appRequestFpsMin,
- &appRequestFpsMax);
+ const auto display = getFirstDisplayToken();
+ gui::DisplayModeSpecs specs;
+ status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &specs);
ASSERT_EQ(res, NO_ERROR);
+ gui::DisplayModeSpecs setSpecs;
std::function<status_t()> condition = [=]() {
- return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode,
- allowGroupSwitching, primaryFpsMin,
- primaryFpsMax, appRequestFpsMin,
- appRequestFpsMax);
+ return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, specs);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
TEST_F(CredentialsTest, SetActiveColorModeTest) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto display = getFirstDisplayToken();
std::function<status_t()> condition = [=]() {
return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
};
@@ -274,7 +275,7 @@
}
TEST_F(CredentialsTest, CaptureTest) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto display = getFirstDisplayToken();
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
DisplayCaptureArgs captureArgs;
@@ -333,14 +334,16 @@
}
TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto display = getFirstDisplayToken();
ASSERT_FALSE(display == nullptr);
bool result = false;
status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
ASSERT_EQ(NO_ERROR, error);
bool hasWideColorMode = false;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
const auto& colorModes = info.supportedColorModes;
for (ColorMode colorMode : colorModes) {
switch (colorMode) {
@@ -357,7 +360,7 @@
}
TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto display = getFirstDisplayToken();
ASSERT_FALSE(display == nullptr);
std::function<status_t()> condition = [=]() {
bool result = false;
@@ -367,10 +370,10 @@
}
TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
- ASSERT_FALSE(display == nullptr);
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
ColorMode colorMode = info.activeColorMode;
ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 2dc96b8..4be961b 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -39,105 +39,71 @@
*/
class RefreshRateRangeTest : public ::testing::Test {
private:
- ui::DisplayModeId initialDefaultMode;
- bool initialAllowGroupSwitching;
- float initialPrimaryMin;
- float initialPrimaryMax;
- float initialAppRequestMin;
- float initialAppRequestMax;
+ gui::DisplayModeSpecs mSpecs;
protected:
void SetUp() override {
- mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
- status_t res =
- SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
- &initialDefaultMode,
- &initialAllowGroupSwitching,
- &initialPrimaryMin,
- &initialPrimaryMax,
- &initialAppRequestMin,
- &initialAppRequestMax);
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ mDisplayId = ids.front().value;
+ mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
ASSERT_EQ(res, NO_ERROR);
}
void TearDown() override {
- status_t res =
- SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode,
- initialAllowGroupSwitching,
- initialPrimaryMin,
- initialPrimaryMax,
- initialAppRequestMin,
- initialAppRequestMax);
+ status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, mSpecs);
ASSERT_EQ(res, NO_ERROR);
}
void testSetAllowGroupSwitching(bool allowGroupSwitching);
sp<IBinder> mDisplayToken;
+ uint64_t mDisplayId;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
ui::DynamicDisplayInfo info;
- status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &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++) {
- res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false,
- modes[i].refreshRate,
- modes[i].refreshRate,
- modes[i].refreshRate,
- modes[i].refreshRate);
+ setSpecs.defaultMode = modes[i].id;
+ setSpecs.primaryRanges.physical.min = modes[i].refreshRate;
+ setSpecs.primaryRanges.physical.max = modes[i].refreshRate;
+ setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+ setSpecs.appRequestRanges = setSpecs.primaryRanges;
+ res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
ASSERT_EQ(res, NO_ERROR);
- ui::DisplayModeId defaultConfig;
- bool allowGroupSwitching;
- float primaryRefreshRateMin;
- float primaryRefreshRateMax;
- float appRequestRefreshRateMin;
- float appRequestRefreshRateMax;
- res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
- &allowGroupSwitching,
- &primaryRefreshRateMin,
- &primaryRefreshRateMax,
- &appRequestRefreshRateMin,
- &appRequestRefreshRateMax);
+ gui::DisplayModeSpecs getSpecs;
+ res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
ASSERT_EQ(res, NO_ERROR);
- ASSERT_EQ(defaultConfig, i);
- ASSERT_EQ(allowGroupSwitching, false);
- ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate);
- ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate);
- ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate);
- ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate);
+ ASSERT_EQ(setSpecs, getSpecs);
}
}
void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
- status_t res =
- SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching,
- 0.f, 90.f, 0.f, 90.f);
- ASSERT_EQ(res, NO_ERROR);
- ui::DisplayModeId defaultConfig;
- bool newAllowGroupSwitching;
- float primaryRefreshRateMin;
- float primaryRefreshRateMax;
- float appRequestRefreshRateMin;
- float appRequestRefreshRateMax;
+ 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;
- res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
- &newAllowGroupSwitching,
- &primaryRefreshRateMin,
- &primaryRefreshRateMax,
- &appRequestRefreshRateMin,
- &appRequestRefreshRateMax);
+ status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
ASSERT_EQ(res, NO_ERROR);
- ASSERT_EQ(defaultConfig, 0);
- ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
- ASSERT_EQ(primaryRefreshRateMin, 0.f);
- ASSERT_EQ(primaryRefreshRateMax, 90.f);
- ASSERT_EQ(appRequestRefreshRateMin, 0.f);
- ASSERT_EQ(appRequestRefreshRateMax, 90.f);
+ gui::DisplayModeSpecs getSpecs;
+ res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
+ ASSERT_EQ(res, NO_ERROR);
+ ASSERT_EQ(setSpecs, getSpecs);
}
TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
@@ -149,4 +115,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 9fa0452..52aa502 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -28,7 +28,9 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(display == nullptr);
mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -177,7 +179,9 @@
}
TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ui::DisplayMode mode;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
const ui::Size& resolution = mode.resolution;
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index c63d251..40a5d57 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -224,7 +224,9 @@
mClient = sp<SurfaceComposerClient>::make();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- mPrimaryDisplay = mClient->getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ mPrimaryDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ui::DisplayMode mode;
mClient->getActiveDisplayMode(mPrimaryDisplay, &mode);
mDisplayWidth = mode.resolution.getWidth();
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 741b6f7..666ce76 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -48,7 +48,7 @@
}
sp<SurfaceControl> makeNotSurfaceControl() {
- return sp<SurfaceControl>::make(mScc, sp<NotALayer>::make(), 1);
+ return sp<SurfaceControl>::make(mScc, sp<NotALayer>::make(), 1, "#1");
}
};
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
index 85108ad..00e134b 100644
--- a/services/surfaceflinger/tests/LayerBorder_test.cpp
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -34,7 +34,9 @@
toHalf3 = ColorTransformHelper::toHalf3;
toHalf4 = ColorTransformHelper::toHalf4;
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(display == nullptr);
mColorOrange = toHalf4({255, 140, 0, 255});
mParentLayer = createColorLayer("Parent layer", Color::RED);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 774c1d7..badd5be 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -289,7 +289,9 @@
private:
void SetUpDisplay() {
- mDisplay = mClient->getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ mDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e1a7ecc..867eddb 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -33,7 +33,9 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(display == nullptr);
ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index faaef5d..e69db7c 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -29,8 +29,10 @@
virtual void SetUp() {
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(display == nullptr);
mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -231,7 +233,10 @@
// Test that the mirror layer is initially offscreen.
TEST_F(MirrorLayerTest, InitialMirrorState) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ui::DisplayMode mode;
SurfaceComposerClient::getActiveDisplayMode(display, &mode);
const ui::Size& size = mode.resolution;
@@ -275,7 +280,9 @@
// Test that a mirror layer can be screenshot when offscreen
TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ui::DisplayMode mode;
SurfaceComposerClient::getActiveDisplayMode(display, &mode);
const ui::Size& size = mode.resolution;
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 1ed6c65..15ff696 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -35,7 +35,9 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 50a4092..9cebf11 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -33,7 +33,9 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(display == nullptr);
// Back layer
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 3a5e532..976ee35 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -30,7 +30,9 @@
LayerTransactionTest::SetUp();
ASSERT_EQ(NO_ERROR, mClient->initCheck());
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ ASSERT_FALSE(ids.empty());
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ASSERT_FALSE(display == nullptr);
ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
deleted file mode 100644
index d79e592..0000000
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
- * Copyright (C) 2016 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <android-base/stringprintf.h>
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <ui/DisplayMode.h>
-
-#include <fstream>
-#include <random>
-#include <thread>
-
-namespace android {
-
-using Transaction = SurfaceComposerClient::Transaction;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Trace = surfaceflinger::Trace;
-using Increment = surfaceflinger::Increment;
-
-constexpr uint32_t BUFFER_UPDATES = 18;
-constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
-constexpr uint32_t SIZE_UPDATE = 134;
-constexpr uint32_t STACK_UPDATE = 1;
-constexpr int32_t RELATIVE_Z = 42;
-constexpr float ALPHA_UPDATE = 0.29f;
-constexpr float CORNER_RADIUS_UPDATE = 0.2f;
-constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24;
-constexpr float POSITION_UPDATE = 121;
-const Rect CROP_UPDATE(16, 16, 32, 32);
-const float SHADOW_RADIUS_UPDATE = 35.0f;
-std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
-
-const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
-constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
-constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
-constexpr auto LAYER_NAME = "Layer Create and Delete Test";
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
- ANativeWindow_Buffer outBuffer;
- sp<Surface> s = sc->getSurface();
- ASSERT_TRUE(s != nullptr);
- ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
- uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; y++) {
- for (int x = 0; x < outBuffer.width; x++) {
- uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
- pixel[0] = r;
- pixel[1] = g;
- pixel[2] = b;
- pixel[3] = 255;
- }
- }
- ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-}
-
-static status_t readProtoFile(Trace* trace) {
- status_t err = NO_ERROR;
-
- int fd = open(DEFAULT_FILENAME, O_RDONLY);
- {
- google::protobuf::io::FileInputStream f(fd);
- if (fd && !trace->ParseFromZeroCopyStream(&f)) {
- err = PERMISSION_DENIED;
- }
- }
- close(fd);
-
- return err;
-}
-
-static void enableInterceptor() {
- system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
-}
-
-static void disableInterceptor() {
- system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
-}
-
-std::string getUniqueName(const std::string& name, const Increment& increment) {
- return base::StringPrintf("%s#%d", name.c_str(), increment.surface_creation().id());
-}
-
-int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
- int32_t layerId = 0;
- for (const auto& increment : capturedTrace.increment()) {
- if (increment.increment_case() == increment.kSurfaceCreation) {
- if (increment.surface_creation().name() == getUniqueName(surfaceName, increment)) {
- layerId = increment.surface_creation().id();
- }
- }
- }
- return layerId;
-}
-
-int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
- int32_t displayId = 0;
- for (const auto& increment : capturedTrace.increment()) {
- if (increment.increment_case() == increment.kDisplayCreation) {
- if (increment.display_creation().name() == displayName) {
- displayId = increment.display_creation().id();
- break;
- }
- }
- }
- return displayId;
-}
-
-class SurfaceInterceptorTest : public ::testing::Test {
-protected:
- void SetUp() override {
- // Allow SurfaceInterceptor write to /data
- system("setenforce 0");
-
- mComposerClient = sp<SurfaceComposerClient>::make();
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
- GTEST_SKIP();
- }
-
- void TearDown() override {
- mComposerClient->dispose();
- mBGSurfaceControl.clear();
- mFGSurfaceControl.clear();
- mComposerClient.clear();
- system("setenforce 1");
- }
-
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mBGSurfaceControl;
- sp<SurfaceControl> mFGSurfaceControl;
- int32_t mBGLayerId;
- int32_t mFGLayerId;
-
-public:
- using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
- using TestAction = void (SurfaceInterceptorTest::*)();
- using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
- using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);
-
- void setupBackgroundSurface();
- void preProcessTrace(const Trace& trace);
-
- // captureTest will enable SurfaceInterceptor, setup background surface,
- // disable SurfaceInterceptor, collect the trace and process the trace for
- // id of background surface before further verification.
- void captureTest(TestTransactionAction action, TestBooleanVerification verification);
- void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
- void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
- void captureTest(TestAction action, TestBooleanVerification verification);
- void captureTest(TestAction action, TestVerification verification);
- void runInTransaction(TestTransactionAction action);
-
- // Verification of changes to a surface
- bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
- bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
- bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
- bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
- bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
- bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
- bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
- bool foundBackgroundBlurRadius);
- bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
- bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
- bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
- bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
- bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
- bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
- bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
- bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
- bool reparentUpdateFound(const SurfaceChange& change, bool found);
- bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
- bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
- bool trustedOverlayUpdateFound(const SurfaceChange& change, bool found);
- bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
-
- // Find all of the updates in the single trace
- void assertAllUpdatesFound(const Trace& trace);
-
- // Verification of creation and deletion of a surface
- bool surfaceCreationFound(const Increment& increment, bool foundSurface);
- bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
- bool foundSurface);
- bool displayCreationFound(const Increment& increment, bool foundDisplay);
- bool displayDeletionFound(const Increment& increment, const int32_t targetId,
- bool foundDisplay);
- bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);
-
- // Verification of buffer updates
- bool bufferUpdatesFound(const Trace& trace);
-
- // Perform each of the possible changes to a surface
- void positionUpdate(Transaction&);
- void sizeUpdate(Transaction&);
- void alphaUpdate(Transaction&);
- void layerUpdate(Transaction&);
- void cropUpdate(Transaction&);
- void cornerRadiusUpdate(Transaction&);
- void backgroundBlurRadiusUpdate(Transaction&);
- void blurRegionsUpdate(Transaction&);
- void matrixUpdate(Transaction&);
- void transparentRegionHintUpdate(Transaction&);
- void layerStackUpdate(Transaction&);
- void hiddenFlagUpdate(Transaction&);
- void opaqueFlagUpdate(Transaction&);
- void secureFlagUpdate(Transaction&);
- void reparentUpdate(Transaction&);
- void relativeParentUpdate(Transaction&);
- void shadowRadiusUpdate(Transaction&);
- void trustedOverlayUpdate(Transaction&);
- void surfaceCreation(Transaction&);
- void displayCreation(Transaction&);
- void displayDeletion(Transaction&);
-
- void nBufferUpdates();
- void runAllUpdates();
-
-private:
- void captureInTransaction(TestTransactionAction action, Trace*);
- void capture(TestAction action, Trace*);
-};
-
-void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
- enableInterceptor();
- setupBackgroundSurface();
- runInTransaction(action);
- disableInterceptor();
- ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
- preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
- enableInterceptor();
- setupBackgroundSurface();
- (this->*action)();
- disableInterceptor();
- ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
- preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::setupBackgroundSurface() {
- const auto display = SurfaceComposerClient::getInternalDisplayToken();
- ASSERT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- const ui::Size& resolution = mode.resolution;
-
- // Background surface
- mBGSurfaceControl =
- mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
- resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mBGSurfaceControl != nullptr);
- ASSERT_TRUE(mBGSurfaceControl->isValid());
-
- // Foreground surface
- mFGSurfaceControl =
- mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
- resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mFGSurfaceControl != nullptr);
- ASSERT_TRUE(mFGSurfaceControl->isValid());
-
- Transaction t;
- t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
- ASSERT_EQ(NO_ERROR,
- t.setLayer(mBGSurfaceControl, INT_MAX - 3)
- .show(mBGSurfaceControl)
- .setLayer(mFGSurfaceControl, INT_MAX - 3)
- .show(mFGSurfaceControl)
- .apply());
-}
-
-void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
- mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME);
- mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME);
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
- TestBooleanVerification verification) {
- Trace capturedTrace;
- captureInTransaction(action, &capturedTrace);
- ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
- Increment::IncrementCase incrementCase) {
- Trace capturedTrace;
- captureInTransaction(action, &capturedTrace);
- ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
- SurfaceChange::SurfaceChangeCase changeCase) {
- Trace capturedTrace;
- captureInTransaction(action, &capturedTrace);
- ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
- Trace capturedTrace;
- capture(action, &capturedTrace);
- ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
- Trace capturedTrace;
- capture(action, &capturedTrace);
- (this->*verification)(capturedTrace);
-}
-
-void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
- Transaction t;
- (this->*action)(t);
- t.apply(true);
-}
-
-void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
- t.setPosition(mBGSurfaceControl, POSITION_UPDATE, POSITION_UPDATE);
-}
-
-void SurfaceInterceptorTest::sizeUpdate(Transaction&) {}
-
-void SurfaceInterceptorTest::alphaUpdate(Transaction& t) {
- t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
-}
-
-void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
- t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
- t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
- BLUR_REGIONS_UPDATE.empty();
- BLUR_REGIONS_UPDATE.push_back(BlurRegion());
- t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
-}
-
-void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
- t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
-}
-
-void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
- t.setCrop(mBGSurfaceControl, CROP_UPDATE);
-}
-
-void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
- t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
-}
-
-void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
- Region region(CROP_UPDATE);
- t.setTransparentRegionHint(mBGSurfaceControl, region);
-}
-
-void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
- t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
-}
-
-void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
- t.setFlags(mBGSurfaceControl, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
-}
-
-void SurfaceInterceptorTest::opaqueFlagUpdate(Transaction& t) {
- t.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
-}
-
-void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) {
- t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
-}
-
-void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
- t.reparent(mBGSurfaceControl, mFGSurfaceControl);
-}
-
-void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
- t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
-}
-
-void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
- t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::trustedOverlayUpdate(Transaction& t) {
- t.setTrustedOverlay(mBGSurfaceControl, true);
-}
-
-void SurfaceInterceptorTest::displayCreation(Transaction&) {
- sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
- SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::displayDeletion(Transaction&) {
- sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
- SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::runAllUpdates() {
- runInTransaction(&SurfaceInterceptorTest::positionUpdate);
- runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
- runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
- runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
- runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
- runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
- runInTransaction(&SurfaceInterceptorTest::layerUpdate);
- runInTransaction(&SurfaceInterceptorTest::cropUpdate);
- runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
- runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
- runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
- runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
- runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
- runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
- runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
- runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
- runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
- runInTransaction(&SurfaceInterceptorTest::trustedOverlayUpdate);
-}
-
-void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
- mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
- PIXEL_FORMAT_RGBA_8888, 0);
-}
-
-void SurfaceInterceptorTest::nBufferUpdates() {
- std::random_device rd;
- std::mt19937_64 gen(rd());
- // This makes testing fun
- std::uniform_int_distribution<uint8_t> dis;
- for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
- fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
- }
-}
-
-bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
- // There should only be one position transaction with x and y = POSITION_UPDATE
- bool hasX(change.position().x() == POSITION_UPDATE);
- bool hasY(change.position().y() == POSITION_UPDATE);
- if (hasX && hasY && !foundPosition) {
- foundPosition = true;
- } else if (hasX && hasY && foundPosition) {
- // Failed because the position update was found a second time
- [] () { FAIL(); }();
- }
- return foundPosition;
-}
-
-bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange&, bool) {
- return true;
-}
-
-bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
- bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
- if (hasAlpha && !foundAlpha) {
- foundAlpha = true;
- } else if (hasAlpha && foundAlpha) {
- [] () { FAIL(); }();
- }
- return foundAlpha;
-}
-
-bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
- bool foundCornerRadius) {
- bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
- if (hasCornerRadius && !foundCornerRadius) {
- foundCornerRadius = true;
- } else if (hasCornerRadius && foundCornerRadius) {
- [] () { FAIL(); }();
- }
- return foundCornerRadius;
-}
-
-bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
- bool foundBackgroundBlur) {
- bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
- BACKGROUND_BLUR_RADIUS_UPDATE);
- if (hasBackgroundBlur && !foundBackgroundBlur) {
- foundBackgroundBlur = true;
- } else if (hasBackgroundBlur && foundBackgroundBlur) {
- []() { FAIL(); }();
- }
- return foundBackgroundBlur;
-}
-
-bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
- bool foundBlurRegions) {
- bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
- if (hasBlurRegions && !foundBlurRegions) {
- foundBlurRegions = true;
- } else if (hasBlurRegions && foundBlurRegions) {
- []() { FAIL(); }();
- }
- return foundBlurRegions;
-}
-
-bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
- bool hasLayer(change.layer().layer() == LAYER_UPDATE);
- if (hasLayer && !foundLayer) {
- foundLayer = true;
- } else if (hasLayer && foundLayer) {
- [] () { FAIL(); }();
- }
- return foundLayer;
-}
-
-bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
- bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
- bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
- bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
- bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
- if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
- foundCrop = true;
- } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
- [] () { FAIL(); }();
- }
- return foundCrop;
-}
-
-bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
- bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
- bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
- bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
- bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
- if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
- foundMatrix = true;
- } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
- [] () { FAIL(); }();
- }
- return foundMatrix;
-}
-
-bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
- bool foundTransparentRegion) {
- auto traceRegion = change.transparent_region_hint().region(0);
- bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
- bool hasTop(traceRegion.top() == CROP_UPDATE.top);
- bool hasRight(traceRegion.right() == CROP_UPDATE.right);
- bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
- if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
- foundTransparentRegion = true;
- } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
- [] () { FAIL(); }();
- }
- return foundTransparentRegion;
-}
-
-bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
- bool foundLayerStack) {
- bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
- if (hasLayerStackUpdate && !foundLayerStack) {
- foundLayerStack = true;
- } else if (hasLayerStackUpdate && foundLayerStack) {
- [] () { FAIL(); }();
- }
- return foundLayerStack;
-}
-
-bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
- bool foundHiddenFlag) {
- bool hasHiddenFlag(change.hidden_flag().hidden_flag());
- if (hasHiddenFlag && !foundHiddenFlag) {
- foundHiddenFlag = true;
- } else if (hasHiddenFlag && foundHiddenFlag) {
- [] () { FAIL(); }();
- }
- return foundHiddenFlag;
-}
-
-bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
- bool foundOpaqueFlag) {
- bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
- if (hasOpaqueFlag && !foundOpaqueFlag) {
- foundOpaqueFlag = true;
- } else if (hasOpaqueFlag && foundOpaqueFlag) {
- [] () { FAIL(); }();
- }
- return foundOpaqueFlag;
-}
-
-bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
- bool foundSecureFlag) {
- bool hasSecureFlag(change.secure_flag().secure_flag());
- if (hasSecureFlag && !foundSecureFlag) {
- foundSecureFlag = true;
- } else if (hasSecureFlag && foundSecureFlag) {
- [] () { FAIL(); }();
- }
- return foundSecureFlag;
-}
-
-bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
- bool hasId(change.reparent().parent_id() == mFGLayerId);
- if (hasId && !found) {
- found = true;
- } else if (hasId && found) {
- []() { FAIL(); }();
- }
- return found;
-}
-
-bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) {
- bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId);
- if (hasId && !found) {
- found = true;
- } else if (hasId && found) {
- []() { FAIL(); }();
- }
- return found;
-}
-
-bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
- bool foundShadowRadius) {
- bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
- if (hasShadowRadius && !foundShadowRadius) {
- foundShadowRadius = true;
- } else if (hasShadowRadius && foundShadowRadius) {
- []() { FAIL(); }();
- }
- return foundShadowRadius;
-}
-
-bool SurfaceInterceptorTest::trustedOverlayUpdateFound(const SurfaceChange& change,
- bool foundTrustedOverlay) {
- bool hasTrustedOverlay(change.trusted_overlay().is_trusted_overlay());
- if (hasTrustedOverlay && !foundTrustedOverlay) {
- foundTrustedOverlay = true;
- } else if (hasTrustedOverlay && foundTrustedOverlay) {
- []() { FAIL(); }();
- }
- return foundTrustedOverlay;
-}
-
-bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
- SurfaceChange::SurfaceChangeCase changeCase) {
- bool foundUpdate = false;
- for (const auto& increment : trace.increment()) {
- if (increment.increment_case() == increment.kTransaction) {
- for (const auto& change : increment.transaction().surface_change()) {
- if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
- switch (changeCase) {
- case SurfaceChange::SurfaceChangeCase::kPosition:
- // foundUpdate is sent for the tests to fail on duplicated increments
- foundUpdate = positionUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kSize:
- foundUpdate = sizeUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kAlpha:
- foundUpdate = alphaUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kLayer:
- foundUpdate = layerUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kCrop:
- foundUpdate = cropUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kCornerRadius:
- foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
- foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kBlurRegions:
- foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kMatrix:
- foundUpdate = matrixUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
- foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kLayerStack:
- foundUpdate = layerStackUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
- foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
- foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kSecureFlag:
- foundUpdate = secureFlagUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kReparent:
- foundUpdate = reparentUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kRelativeParent:
- foundUpdate = relativeParentUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kShadowRadius:
- foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::kTrustedOverlay:
- foundUpdate = trustedOverlayUpdateFound(change, foundUpdate);
- break;
- case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
- break;
- }
- }
- }
- }
- }
- return foundUpdate;
-}
-
-void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
-}
-
-bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
- bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
- if (isMatch && !foundSurface) {
- foundSurface = true;
- } else if (isMatch && foundSurface) {
- [] () { FAIL(); }();
- }
- return foundSurface;
-}
-
-bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
- const int32_t targetId, bool foundSurface) {
- bool isMatch(increment.surface_deletion().id() == targetId);
- if (isMatch && !foundSurface) {
- foundSurface = true;
- } else if (isMatch && foundSurface) {
- [] () { FAIL(); }();
- }
- return foundSurface;
-}
-
-bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
- bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
- !increment.display_creation().is_secure());
- if (isMatch && !foundDisplay) {
- foundDisplay = true;
- } else if (isMatch && foundDisplay) {
- [] () { FAIL(); }();
- }
- return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
- const int32_t targetId, bool foundDisplay) {
- bool isMatch(increment.display_deletion().id() == targetId);
- if (isMatch && !foundDisplay) {
- foundDisplay = true;
- } else if (isMatch && foundDisplay) {
- [] () { FAIL(); }();
- }
- return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
- Increment::IncrementCase incrementCase) {
- bool foundIncrement = false;
- for (const auto& increment : trace.increment()) {
- if (increment.increment_case() == incrementCase) {
- int32_t targetId = 0;
- switch (incrementCase) {
- case Increment::IncrementCase::kSurfaceCreation:
- foundIncrement = surfaceCreationFound(increment, foundIncrement);
- break;
- case Increment::IncrementCase::kSurfaceDeletion:
- // Find the id of created surface.
- targetId = getSurfaceId(trace, LAYER_NAME);
- foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
- break;
- case Increment::IncrementCase::kDisplayCreation:
- foundIncrement = displayCreationFound(increment, foundIncrement);
- break;
- case Increment::IncrementCase::kDisplayDeletion:
- // Find the id of created display.
- targetId = getDisplayId(trace, DISPLAY_NAME.string());
- foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
- break;
- default:
- /* code */
- break;
- }
- }
- }
- return foundIncrement;
-}
-
-bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
- uint32_t updates = 0;
- for (const auto& inc : trace.increment()) {
- if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
- updates++;
- }
- }
- return updates == BUFFER_UPDATES;
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::positionUpdate,
- SurfaceChange::SurfaceChangeCase::kPosition);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
- SurfaceChange::SurfaceChangeCase::kCornerRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
- SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
- SurfaceChange::SurfaceChangeCase::kBlurRegions);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
- SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::layerStackUpdate,
- SurfaceChange::SurfaceChangeCase::kLayerStack);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
- SurfaceChange::SurfaceChangeCase::kHiddenFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
- SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
- SurfaceChange::SurfaceChangeCase::kSecureFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::reparentUpdate,
- SurfaceChange::SurfaceChangeCase::kReparent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
- SurfaceChange::SurfaceChangeCase::kRelativeParent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
- SurfaceChange::SurfaceChangeCase::kShadowRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTrustedOverlayUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::trustedOverlayUpdate,
- SurfaceChange::SurfaceChangeCase::kTrustedOverlay);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
- captureTest(&SurfaceInterceptorTest::runAllUpdates,
- &SurfaceInterceptorTest::assertAllUpdatesFound);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
- captureTest(&SurfaceInterceptorTest::surfaceCreation,
- Increment::IncrementCase::kSurfaceCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
- captureTest(&SurfaceInterceptorTest::displayCreation,
- Increment::IncrementCase::kDisplayCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
- enableInterceptor();
- runInTransaction(&SurfaceInterceptorTest::displayDeletion);
- disableInterceptor();
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
-}
-
-// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
-// first create a snapshot of the existing displays and surfaces and then start capturing
-// the buffer updates
-TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
- setupBackgroundSurface();
- std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
- enableInterceptor();
- disableInterceptor();
- bufferUpdates.join();
-
- Trace capturedTrace;
- ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
- const auto& firstIncrement = capturedTrace.mutable_increment(0);
- ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
-}
-}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index ad03ed3..797a64c 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -35,7 +35,10 @@
return mDelegate->screenshot();
case RenderPath::VIRTUAL_DISPLAY:
- const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ const auto displayToken = ids.empty()
+ ? nullptr
+ : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
ui::DisplayState displayState;
SurfaceComposerClient::getDisplayState(displayToken, &displayState);
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
deleted file mode 100644
index 06afdb1..0000000
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ /dev/null
@@ -1,65 +0,0 @@
-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_test {
- name: "sffakehwc_test",
- defaults: [
- "android.hardware.graphics.composer3-ndk_shared",
- "surfaceflinger_defaults",
- ],
- test_suites: ["device-tests"],
- srcs: [
- "FakeComposerClient.cpp",
- "FakeComposerService.cpp",
- "FakeComposerUtils.cpp",
- "SFFakeHwc_test.cpp",
- ],
- require_root: true,
- shared_libs: [
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- "android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
- "android.hardware.power@1.3",
- "android.hardware.power-V2-cpp",
- "libbase",
- "libbinder",
- "libbinder_ndk",
- "libcutils",
- "libfmq",
- "libgui",
- "libhidlbase",
- "liblayers_proto",
- "liblog",
- "libnativewindow",
- "libsync",
- "libtimestats",
- "libui",
- "libutils",
- ],
- static_libs: [
- "android.hardware.graphics.composer@2.1-resources",
- "libaidlcommonsupport",
- "libcompositionengine",
- "libgmock",
- "libperfetto_client_experimental",
- "librenderengine",
- "libtrace_proto",
- "libaidlcommonsupport",
- ],
- header_libs: [
- "android.hardware.graphics.composer@2.4-command-buffer",
- "android.hardware.graphics.composer@2.4-hal",
- "android.hardware.graphics.composer3-command-buffer",
- "libsurfaceflinger_headers",
- ],
-}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
deleted file mode 100644
index a5cca35..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ /dev/null
@@ -1,929 +0,0 @@
-/*
- * Copyright (C) 2017 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeComposer"
-
-#include "FakeComposerClient.h"
-
-#include <gui/SurfaceComposerClient.h>
-
-#include <log/log.h>
-
-#include <gtest/gtest.h>
-
-#include <inttypes.h>
-#include <time.h>
-#include <algorithm>
-#include <condition_variable>
-#include <iostream>
-#include <mutex>
-#include <set>
-#include <thread>
-
-constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
-
-using namespace sftest;
-
-using android::Condition;
-using android::Mutex;
-
-using Clock = std::chrono::steady_clock;
-using TimePoint = std::chrono::time_point<Clock>;
-
-namespace {
-
-// Internal state of a layer in the HWC API.
-class LayerImpl {
-public:
- LayerImpl() = default;
-
- bool mValid = true;
- RenderState mRenderState;
- uint32_t mZ = 0;
-};
-
-// Struct for storing per frame rectangle state. Contains the render
-// state shared to the test case. Basically a snapshot and a subset of
-// LayerImpl sufficient to re-create the pixels of a layer for the
-// frame.
-struct FrameRect {
-public:
- FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
- : layer(layer_), renderState(state), z(z_) {}
-
- const Layer layer;
- const RenderState renderState;
- const uint32_t z;
-};
-
-// Collection of FrameRects forming one rendered frame. Could store
-// related fences and other data in the future.
-class Frame {
-public:
- Frame() = default;
- std::vector<std::unique_ptr<FrameRect>> rectangles;
-};
-
-class DelayedEventGenerator {
-public:
- explicit DelayedEventGenerator(std::function<void()> onTimerExpired)
- : mOnTimerExpired(onTimerExpired) {
- mThread = std::thread([this]() { loop(); });
- }
-
- ~DelayedEventGenerator() {
- ALOGI("DelayedEventGenerator exiting.");
- {
- std::unique_lock<std::mutex> lock(mMutex);
- mRunning = false;
- mWakeups.clear();
- mCondition.notify_one();
- }
- mThread.join();
- ALOGI("DelayedEventGenerator exited.");
- }
-
- void wakeAfter(std::chrono::nanoseconds waitTime) {
- std::unique_lock<std::mutex> lock(mMutex);
- mWakeups.insert(Clock::now() + waitTime);
- mCondition.notify_one();
- }
-
-private:
- void loop() {
- while (true) {
- // Lock scope
- {
- std::unique_lock<std::mutex> lock(mMutex);
- mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
- if (!mRunning && mWakeups.empty()) {
- // This thread should only exit once the destructor has been called and all
- // wakeups have been processed
- return;
- }
-
- // At this point, mWakeups will not be empty
-
- TimePoint target = *(mWakeups.begin());
- auto status = mCondition.wait_until(lock, target);
- while (status == std::cv_status::no_timeout) {
- // This was either a spurious wakeup or another wakeup was added, so grab the
- // oldest point and wait again
- target = *(mWakeups.begin());
- status = mCondition.wait_until(lock, target);
- }
-
- // status must have been timeout, so we can finally clear this point
- mWakeups.erase(target);
- }
- // Callback *without* locks!
- mOnTimerExpired();
- }
- }
-
- std::function<void()> mOnTimerExpired;
- std::thread mThread;
- std::mutex mMutex;
- std::condition_variable mCondition;
- bool mRunning = true;
- std::set<TimePoint> mWakeups;
-};
-
-} // namespace
-
-FakeComposerClient::FakeComposerClient()
- : mEventCallback(nullptr),
- mEventCallback_2_4(nullptr),
- mCurrentConfig(NULL_DISPLAY_CONFIG),
- mVsyncEnabled(false),
- mLayers(),
- mDelayedEventGenerator(
- std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
- mSurfaceComposer(nullptr) {}
-
-FakeComposerClient::~FakeComposerClient() {}
-
-bool FakeComposerClient::hasCapability(hwc2_capability_t /*capability*/) {
- return false;
-}
-
-std::string FakeComposerClient::dumpDebugInfo() {
- return {};
-}
-
-void FakeComposerClient::registerEventCallback(EventCallback* callback) {
- ALOGV("registerEventCallback");
- LOG_FATAL_IF(mEventCallback_2_4 != nullptr,
- "already registered using registerEventCallback_2_4");
-
- mEventCallback = callback;
- if (mEventCallback) {
- mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
- }
-}
-
-void FakeComposerClient::unregisterEventCallback() {
- ALOGV("unregisterEventCallback");
- mEventCallback = nullptr;
-}
-
-void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
- if (mEventCallback) {
- mEventCallback->onHotplug(display, state);
- } else if (mEventCallback_2_4) {
- mEventCallback_2_4->onHotplug(display, state);
- }
-}
-
-void FakeComposerClient::refreshDisplay(Display display) {
- if (mEventCallback) {
- mEventCallback->onRefresh(display);
- } else if (mEventCallback_2_4) {
- mEventCallback_2_4->onRefresh(display);
- }
-}
-
-uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
- ALOGV("getMaxVirtualDisplayCount");
- return 1;
-}
-
-V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
- V1_0::PixelFormat* /*format*/,
- Display* /*outDisplay*/) {
- ALOGV("createVirtualDisplay");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
- ALOGV("destroyVirtualDisplay");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
- ALOGV("createLayer");
- *outLayer = mLayers.size();
- auto newLayer = std::make_unique<LayerImpl>();
- mLayers.push_back(std::move(newLayer));
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
- ALOGV("destroyLayer");
- mLayers[layer]->mValid = false;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) {
- ALOGV("getActiveConfig");
- if (mMockHal) {
- return mMockHal->getActiveConfig(display, outConfig);
- }
-
- // TODO Assert outConfig != nullptr
-
- // TODO This is my reading of the
- // IComposerClient::getActiveConfig, but returning BAD_CONFIG
- // seems to not fit SurfaceFlinger plans. See version 2 below.
- // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
- // return V2_1::Error::BAD_CONFIG;
- // }
- //*outConfig = mCurrentConfig;
- *outConfig = 1; // Very special config for you my friend
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
- uint32_t /*height*/,
- V1_0::PixelFormat /*format*/,
- V1_0::Dataspace /*dataspace*/) {
- ALOGV("getClientTargetSupport");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getColorModes(Display /*display*/,
- hidl_vec<V1_0::ColorMode>* /*outModes*/) {
- ALOGV("getColorModes");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
- V2_1::IComposerClient::Attribute attribute,
- int32_t* outValue) {
- auto tmpError =
- getDisplayAttribute_2_4(display, config,
- static_cast<IComposerClient::Attribute>(attribute), outValue);
- return static_cast<V2_1::Error>(tmpError);
-}
-
-V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
- ALOGV("getDisplayConfigs");
- if (mMockHal) {
- return mMockHal->getDisplayConfigs(display, outConfigs);
- }
-
- // TODO assert display == 1, outConfigs != nullptr
-
- outConfigs->resize(1);
- (*outConfigs)[0] = 1;
-
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
- ALOGV("getDisplayName");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/,
- IComposerClient::DisplayType* outType) {
- ALOGV("getDisplayType");
- // TODO: This setting nothing on the output had no effect on initial trials. Is first display
- // assumed to be physical?
- *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
- ALOGV("getDozeSupport");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/,
- hidl_vec<V1_0::Hdr>* /*outTypes*/,
- float* /*outMaxLuminance*/,
- float* /*outMaxAverageLuminance*/,
- float* /*outMinLuminance*/) {
- ALOGV("getHdrCapabilities");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) {
- ALOGV("setActiveConfig");
- if (mMockHal) {
- return mMockHal->setActiveConfig(display, config);
- }
- mCurrentConfig = config;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) {
- ALOGV("setColorMode");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/,
- V2_1::IComposerClient::PowerMode /*mode*/) {
- ALOGV("setPowerMode");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/,
- IComposerClient::Vsync enabled) {
- mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
- ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
- int32_t /*hint*/) {
- ALOGV("setColorTransform");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
- int32_t /*acquireFence*/, int32_t /*dataspace*/,
- const std::vector<hwc_rect_t>& /*damage*/) {
- ALOGV("setClientTarget");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
- int32_t /*releaseFence*/) {
- ALOGV("setOutputBuffer");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::validateDisplay(
- Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
- std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
- uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
- std::vector<uint32_t>* /*outRequestMasks*/) {
- ALOGV("validateDisplay");
- // TODO: Assume touching nothing means All Korrekt!
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
- ALOGV("acceptDisplayChanges");
- // Didn't ask for changes because software is omnipotent.
- return V2_1::Error::NONE;
-}
-
-bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
- return a->z <= b->z;
-}
-
-V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
- std::vector<Layer>* /*outLayers*/,
- std::vector<int32_t>* /*outReleaseFences*/) {
- ALOGV("presentDisplay");
- // TODO Leaving layers and their fences out for now. Doing so
- // means that we've already processed everything. Important to
- // test that the fences are respected, though. (How?)
-
- std::unique_ptr<Frame> newFrame(new Frame);
- for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
- const LayerImpl& layerImpl = *mLayers[layer];
-
- if (!layerImpl.mValid) continue;
-
- auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
- newFrame->rectangles.push_back(std::move(rect));
- }
- std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
- {
- Mutex::Autolock _l(mStateMutex);
- mFrames.push_back(std::move(newFrame));
- mFramesAvailable.broadcast();
- }
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
- int32_t /*x*/, int32_t /*y*/) {
- ALOGV("setLayerCursorPosition");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer,
- buffer_handle_t buffer, int32_t acquireFence) {
- ALOGV("setLayerBuffer");
- LayerImpl& l = getLayerImpl(layer);
- if (buffer != l.mRenderState.mBuffer) {
- l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
- }
- l.mRenderState.mBuffer = buffer;
- l.mRenderState.mAcquireFence = acquireFence;
-
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
- const std::vector<hwc_rect_t>& /*damage*/) {
- ALOGV("setLayerSurfaceDamage");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
- ALOGV("setLayerBlendMode");
- getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
- IComposerClient::Color color) {
- ALOGV("setLayerColor");
- getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
- getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
- getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
- getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
- int32_t /*type*/) {
- ALOGV("setLayerCompositionType");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
- int32_t /*dataspace*/) {
- ALOGV("setLayerDataspace");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
- const hwc_rect_t& frame) {
- ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
- frame.bottom);
- getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
- ALOGV("setLayerPlaneAlpha");
- getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
- buffer_handle_t /*stream*/) {
- ALOGV("setLayerSidebandStream");
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
- const hwc_frect_t& crop) {
- ALOGV("setLayerSourceCrop");
- getLayerImpl(layer).mRenderState.mSourceCrop = crop;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer,
- int32_t transform) {
- ALOGV("setLayerTransform");
- getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
- const std::vector<hwc_rect_t>& visible) {
- ALOGV("setLayerVisibleRegion");
- getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
- return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
- ALOGV("setLayerZOrder");
- getLayerImpl(layer).mZ = z;
- return V2_1::Error::NONE;
-}
-
-// Composer 2.2
-V2_1::Error FakeComposerClient::getPerFrameMetadataKeys(
- Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadata(
- Display /*display*/, Layer /*layer*/,
- const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferAttributes(
- Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/,
- graphics::common::V1_1::Dataspace* /*outDataspace*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/,
- const native_handle_t* /*bufferHandle*/,
- android::base::unique_fd /*fenceFd*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/,
- android::base::unique_fd* /*outFenceFd*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::createVirtualDisplay_2_2(
- uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/,
- Display* /*outDisplay*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-V2_1::Error FakeComposerClient::getClientTargetSupport_2_2(
- Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
- graphics::common::V1_1::PixelFormat /*format*/,
- graphics::common::V1_1::Dataspace /*dataspace*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/,
- V2_2::IComposerClient::PowerMode /*mode*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/,
- V2_2::IComposerClient::FloatColor /*color*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getColorModes_2_2(
- Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getRenderIntents(
- Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/,
- std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/,
- graphics::common::V1_1::ColorMode /*mode*/,
- graphics::common::V1_1::RenderIntent /*intent*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix(
- graphics::common::V1_1::Dataspace /*dataspace*/) {
- return {};
-}
-
-// Composer 2.3
-V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3(
- Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/,
- graphics::common::V1_2::ColorMode /*mode*/,
- graphics::common::V1_1::RenderIntent /*intent*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getRenderIntents_2_3(
- Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/,
- std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getColorModes_2_3(
- Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getClientTargetSupport_2_3(
- Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
- graphics::common::V1_2::PixelFormat /*format*/,
- graphics::common::V1_2::Dataspace /*dataspace*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3(
- Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/,
- graphics::common::V1_2::Dataspace* /*outDataspace*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getHdrCapabilities_2_3(
- Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/,
- float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3(
- Display /*display*/, Layer /*layer*/,
- const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/,
- uint8_t* /*outPort*/,
- std::vector<uint8_t>* /*outData*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/,
- const float* /*matrix*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes(
- uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/,
- graphics::common::V1_2::Dataspace& /*dataspace*/,
- hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled(
- uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/,
- hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/,
- uint64_t /*maxFrames*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayedContentSample(
- uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/,
- uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/,
- hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/,
- hidl_vec<uint64_t>& /*sampleComponent3*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayCapabilities(
- Display /*display*/,
- std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs(
- Display /*display*/, Layer /*layer*/,
- std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/,
- bool* /*outSupport*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) {
- return V2_1::Error::UNSUPPORTED;
-}
-
-// Composer 2.4
-void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) {
- ALOGV("registerEventCallback_2_4");
- LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback");
-
- mEventCallback_2_4 = callback;
- if (mEventCallback_2_4) {
- mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
- }
-}
-
-void FakeComposerClient::unregisterEventCallback_2_4() {
- ALOGV("unregisterEventCallback_2_4");
- mEventCallback_2_4 = nullptr;
-}
-
-V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4(
- Display /*display*/,
- std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getDisplayConnectionType(
- Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) {
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) {
- ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
- static_cast<int>(config), static_cast<int>(attribute), outValue);
- if (mMockHal) {
- return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue);
- }
-
- // TODO: SOOO much fun to be had with these alone
- switch (attribute) {
- case IComposerClient::Attribute::WIDTH:
- *outValue = 1920;
- break;
- case IComposerClient::Attribute::HEIGHT:
- *outValue = 1080;
- break;
- case IComposerClient::Attribute::VSYNC_PERIOD:
- *outValue = 1666666666;
- break; // TOOD: Tests break down if lowered to 16ms?
- case IComposerClient::Attribute::DPI_X:
- *outValue = 240;
- break;
- case IComposerClient::Attribute::DPI_Y:
- *outValue = 240;
- break;
- default:
- LOG_ALWAYS_FATAL("Say what!?! New attribute");
- }
-
- return Error::NONE;
-}
-
-V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display,
- V2_4::VsyncPeriodNanos* outVsyncPeriod) {
- ALOGV("getDisplayVsyncPeriod");
- if (mMockHal) {
- return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod);
- }
-
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setActiveConfigWithConstraints(
- Display display, Config config,
- const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- VsyncPeriodChangeTimeline* timeline) {
- ALOGV("setActiveConfigWithConstraints");
- if (mMockHal) {
- return mMockHal->setActiveConfigWithConstraints(display, config,
- vsyncPeriodChangeConstraints, timeline);
- }
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
- ALOGV("setAutoLowLatencyMode");
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getSupportedContentTypes(
- Display, std::vector<IComposerClient::ContentType>*) {
- ALOGV("getSupportedContentTypes");
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
- ALOGV("setContentType");
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::validateDisplay_2_4(
- Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
- std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
- uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
- std::vector<uint32_t>* /*outRequestMasks*/,
- IComposerClient::ClientTargetProperty* /*outClientTargetProperty*/) {
- return V2_4::Error::NONE;
-}
-
-V2_4::Error FakeComposerClient::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
- const std::vector<uint8_t>&) {
- ALOGV("setLayerGenericMetadata");
- return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getLayerGenericMetadataKeys(
- std::vector<IComposerClient::LayerGenericMetadataKey>*) {
- ALOGV("getLayerGenericMetadataKeys");
- return V2_4::Error::UNSUPPORTED;
-}
-
-//////////////////////////////////////////////////////////////////
-
-void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
- if (mEventCallback || mEventCallback_2_4) {
- uint64_t timestamp = vsyncTime;
- ALOGV("Vsync");
- if (timestamp == 0) {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
- }
- if (mSurfaceComposer != nullptr) {
- mSurfaceComposer->injectVSync(timestamp);
- } else if (mEventCallback) {
- mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp);
- } else {
- mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666);
- }
- }
-}
-
-void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
- mDelayedEventGenerator->wakeAfter(wait);
-}
-
-LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
- // TODO Change these to an internal state check that can be
- // invoked from the gtest? GTest macros do not seem all that safe
- // when used outside the test class
- EXPECT_GE(handle, static_cast<Layer>(0));
- EXPECT_LT(handle, mLayers.size());
- return *(mLayers[handle]);
-}
-
-int FakeComposerClient::getFrameCount() const {
- return mFrames.size();
-}
-
-static std::vector<RenderState> extractRenderState(
- const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
- std::vector<RenderState> result;
- result.reserve(internalRects.size());
- for (const std::unique_ptr<FrameRect>& rect : internalRects) {
- result.push_back(rect->renderState);
- }
- return result;
-}
-
-std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
- Mutex::Autolock _l(mStateMutex);
- return extractRenderState(mFrames[frame]->rectangles);
-}
-
-std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
- Mutex::Autolock _l(mStateMutex);
- return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
-}
-
-void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
- int currentFrame = 0;
- {
- Mutex::Autolock _l(mStateMutex); // I hope this is ok...
- currentFrame = static_cast<int>(mFrames.size());
- requestVSync();
- }
- waitUntilFrame(currentFrame + 1, maxWait);
-}
-
-void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
- Mutex::Autolock _l(mStateMutex);
- while (mFrames.size() < static_cast<size_t>(targetFrame)) {
- android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
- if (result == android::TIMED_OUT) {
- ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
- mFrames.size(), maxWait.count());
- return;
- }
- }
-}
-
-void FakeComposerClient::clearFrames() {
- Mutex::Autolock _l(mStateMutex);
- mFrames.clear();
- for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
- if (layer->mValid) {
- layer->mRenderState.mSwapCount = 0;
- }
- }
-}
-
-void FakeComposerClient::onSurfaceFlingerStart() {
- mSurfaceComposer = nullptr;
- do {
- mSurfaceComposer = android::sp<android::SurfaceComposerClient>::make();
- android::status_t initResult = mSurfaceComposer->initCheck();
- if (initResult != android::NO_ERROR) {
- ALOGD("Init result: %d", initResult);
- mSurfaceComposer = nullptr;
- std::this_thread::sleep_for(10ms);
- }
- } while (mSurfaceComposer == nullptr);
- ALOGD("SurfaceComposerClient created");
- mSurfaceComposer->enableVSyncInjections(true);
-}
-
-void FakeComposerClient::onSurfaceFlingerStop() {
- mSurfaceComposer->enableVSyncInjections(false);
- mSurfaceComposer->dispose();
- mSurfaceComposer.clear();
-}
-
-// Includes destroyed layers, stored in order of creation.
-int FakeComposerClient::getLayerCount() const {
- return mLayers.size();
-}
-
-Layer FakeComposerClient::getLayer(size_t index) const {
- // NOTE: If/when passing calls through to actual implementation,
- // this might get more involving.
- return static_cast<Layer>(index);
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
deleted file mode 100644
index 600e765..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2017 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 <chrono>
-
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-#include <composer-hal/2.4/ComposerClient.h>
-#include <utils/Condition.h>
-
-#include "MockComposerHal.h"
-#include "RenderState.h"
-
-using namespace android::hardware::graphics::common;
-using namespace android::hardware::graphics::composer;
-using namespace android::hardware::graphics::composer::V2_4;
-using namespace android::hardware::graphics::composer::V2_4::hal;
-using namespace android::hardware;
-using namespace std::chrono_literals;
-
-namespace {
-class LayerImpl;
-class Frame;
-class DelayedEventGenerator;
-} // namespace
-
-namespace android {
-class SurfaceComposerClient;
-} // namespace android
-
-namespace sftest {
-// NOTE: The ID's need to be exactly these. VR composer and parts of
-// the SurfaceFlinger assume the display IDs to have these values
-// despite the enum being documented as a display type.
-// TODO: Reference to actual documentation
-constexpr Display PRIMARY_DISPLAY = static_cast<Display>(HWC_DISPLAY_PRIMARY);
-constexpr Display EXTERNAL_DISPLAY = static_cast<Display>(HWC_DISPLAY_EXTERNAL);
-
-class FakeComposerClient : public ComposerHal {
-public:
- FakeComposerClient();
- virtual ~FakeComposerClient();
-
- void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; }
-
- bool hasCapability(hwc2_capability_t capability) override;
-
- std::string dumpDebugInfo() override;
- void registerEventCallback(EventCallback* callback) override;
- void unregisterEventCallback() override;
-
- uint32_t getMaxVirtualDisplayCount() override;
- V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format,
- Display* outDisplay) override;
- V2_1::Error destroyVirtualDisplay(Display display) override;
- V2_1::Error createLayer(Display display, Layer* outLayer) override;
- V2_1::Error destroyLayer(Display display, Layer layer) override;
-
- V2_1::Error getActiveConfig(Display display, Config* outConfig) override;
- V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
- V1_0::PixelFormat format,
- V1_0::Dataspace dataspace) override;
- V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override;
- V2_1::Error getDisplayAttribute(Display display, Config config,
- V2_1::IComposerClient::Attribute attribute,
- int32_t* outValue) override;
- V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
- V2_1::Error getDisplayName(Display display, hidl_string* outName) override;
- V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
- V2_1::Error getDozeSupport(Display display, bool* outSupport) override;
- V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes,
- float* outMaxLuminance, float* outMaxAverageLuminance,
- float* outMinLuminance) override;
-
- V2_1::Error setActiveConfig(Display display, Config config) override;
- V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override;
- V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override;
- V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
- V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
- V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
- int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
- V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer,
- int32_t releaseFence) override;
- V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask,
- std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks) override;
- V2_1::Error acceptDisplayChanges(Display display) override;
- V2_1::Error presentDisplay(Display display, int32_t* outPresentFence,
- std::vector<Layer>* outLayers,
- std::vector<int32_t>* outReleaseFences) override;
-
- V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
- V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
- int32_t acquireFence) override;
- V2_1::Error setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<hwc_rect_t>& damage) override;
- V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
- V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
- V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
- V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
- V2_1::Error setLayerDisplayFrame(Display display, Layer layer,
- const hwc_rect_t& frame) override;
- V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
- V2_1::Error setLayerSidebandStream(Display display, Layer layer,
- buffer_handle_t stream) override;
- V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
- V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
- V2_1::Error setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<hwc_rect_t>& visible) override;
- V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
- // Composer 2.2
- V2_1::Error getPerFrameMetadataKeys(
- Display display,
- std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override;
- V2_1::Error setLayerPerFrameMetadata(
- Display display, Layer layer,
- const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override;
-
- V2_1::Error getReadbackBufferAttributes(
- Display display, graphics::common::V1_1::PixelFormat* outFormat,
- graphics::common::V1_1::Dataspace* outDataspace) override;
- V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
- android::base::unique_fd fenceFd) override;
- V2_1::Error getReadbackBufferFence(Display display,
- android::base::unique_fd* outFenceFd) override;
- V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
- graphics::common::V1_1::PixelFormat* format,
- Display* outDisplay) override;
- V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
- graphics::common::V1_1::PixelFormat format,
- graphics::common::V1_1::Dataspace dataspace) override;
- V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override;
-
- V2_1::Error setLayerFloatColor(Display display, Layer layer,
- V2_2::IComposerClient::FloatColor color) override;
-
- V2_1::Error getColorModes_2_2(Display display,
- hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override;
- V2_1::Error getRenderIntents(
- Display display, graphics::common::V1_1::ColorMode mode,
- std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
- V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode,
- graphics::common::V1_1::RenderIntent intent) override;
-
- std::array<float, 16> getDataspaceSaturationMatrix(
- graphics::common::V1_1::Dataspace dataspace) override;
-
- // Composer 2.3
- V2_1::Error getPerFrameMetadataKeys_2_3(
- Display display,
- std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override;
-
- V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode,
- graphics::common::V1_1::RenderIntent intent) override;
-
- V2_1::Error getRenderIntents_2_3(
- Display display, graphics::common::V1_2::ColorMode mode,
- std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
-
- V2_1::Error getColorModes_2_3(Display display,
- hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override;
-
- V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height,
- graphics::common::V1_2::PixelFormat format,
- graphics::common::V1_2::Dataspace dataspace) override;
- V2_1::Error getReadbackBufferAttributes_2_3(
- Display display, graphics::common::V1_2::PixelFormat* outFormat,
- graphics::common::V1_2::Dataspace* outDataspace) override;
- V2_1::Error getHdrCapabilities_2_3(Display display,
- hidl_vec<graphics::common::V1_2::Hdr>* outTypes,
- float* outMaxLuminance, float* outMaxAverageLuminance,
- float* outMinLuminance) override;
- V2_1::Error setLayerPerFrameMetadata_2_3(
- Display display, Layer layer,
- const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override;
- V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) override;
- V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
- V2_1::Error getDisplayedContentSamplingAttributes(
- uint64_t display, graphics::common::V1_2::PixelFormat& format,
- graphics::common::V1_2::Dataspace& dataspace,
- hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override;
- V2_1::Error setDisplayedContentSamplingEnabled(
- uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable,
- hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask,
- uint64_t maxFrames) override;
- V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
- uint64_t& frameCount,
- hidl_vec<uint64_t>& sampleComponent0,
- hidl_vec<uint64_t>& sampleComponent1,
- hidl_vec<uint64_t>& sampleComponent2,
- hidl_vec<uint64_t>& sampleComponent3) override;
- V2_1::Error getDisplayCapabilities(
- Display display,
- std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override;
- V2_1::Error setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override;
- V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
- V2_1::Error setDisplayBrightness(Display display, float brightness) override;
-
- // Composer 2.4
- void registerEventCallback_2_4(EventCallback_2_4* callback) override;
-
- void unregisterEventCallback_2_4() override;
-
- V2_4::Error getDisplayCapabilities_2_4(
- Display display,
- std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override;
- V2_4::Error getDisplayConnectionType(
- Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override;
- V2_4::Error getDisplayAttribute_2_4(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) override;
- V2_4::Error getDisplayVsyncPeriod(Display display,
- V2_4::VsyncPeriodNanos* outVsyncPeriod) override;
- V2_4::Error setActiveConfigWithConstraints(
- Display display, Config config,
- const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- VsyncPeriodChangeTimeline* outTimeline) override;
- V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
- V2_4::Error getSupportedContentTypes(
- Display display,
- std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
- V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
- V2_4::Error validateDisplay_2_4(
- Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks,
- IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
- V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
- bool mandatory, const std::vector<uint8_t>& value) override;
- V2_4::Error getLayerGenericMetadataKeys(
- std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
-
- void setClient(ComposerClient* client);
-
- void requestVSync(uint64_t vsyncTime = 0);
- // We don't want tests hanging, so always use a timeout. Remember
- // to always check the number of frames with test ASSERT_!
- // Wait until next frame is rendered after requesting vsync.
- void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
- void runVSyncAfter(std::chrono::nanoseconds wait);
-
- int getFrameCount() const;
- // We don't want tests hanging, so always use a timeout. Remember
- // to always check the number of frames with test ASSERT_!
- void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
- std::vector<RenderState> getFrameRects(int frame) const;
- std::vector<RenderState> getLatestFrame() const;
- void clearFrames();
-
- void onSurfaceFlingerStart();
- void onSurfaceFlingerStop();
-
- int getLayerCount() const;
- Layer getLayer(size_t index) const;
-
- void hotplugDisplay(Display display, IComposerCallback::Connection state);
- void refreshDisplay(Display display);
-
-private:
- LayerImpl& getLayerImpl(Layer handle);
-
- EventCallback* mEventCallback;
- EventCallback_2_4* mEventCallback_2_4;
- Config mCurrentConfig;
- bool mVsyncEnabled;
- std::vector<std::unique_ptr<LayerImpl>> mLayers;
- std::vector<std::unique_ptr<Frame>> mFrames;
- // Using a pointer to hide the implementation into the CPP file.
- std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
- android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
- mutable android::Mutex mStateMutex;
- mutable android::Condition mFramesAvailable;
-
- MockComposerHal* mMockHal = nullptr;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
deleted file mode 100644
index c656eed..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2017 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcService"
-#include <log/log.h>
-
-#include "FakeComposerService.h"
-
-using namespace android::hardware;
-using namespace android::hardware::graphics::composer;
-
-namespace sftest {
-
-FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client)
- : mClient(client) {}
-
-FakeComposerService_2_1::~FakeComposerService_2_1() {
- ALOGI("Maybe killing client %p", mClient.get());
- // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) {
- ALOGI("FakeComposerService::getCapabilities");
- hidl_cb(hidl_vec<Capability>());
- return Void();
-}
-
-Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
- ALOGI("FakeComposerService::dumpDebugInfo");
- hidl_cb(hidl_string());
- return Void();
-}
-
-Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) {
- ALOGI("FakeComposerService::createClient %p", mClient.get());
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_1::Error::NONE, mClient);
- return Void();
-}
-
-FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client)
- : mClient(client) {}
-
-FakeComposerService_2_2::~FakeComposerService_2_2() {
- ALOGI("Maybe killing client %p", mClient.get());
- // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) {
- ALOGI("FakeComposerService::getCapabilities");
- hidl_cb(hidl_vec<Capability>());
- return Void();
-}
-
-Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
- ALOGI("FakeComposerService::dumpDebugInfo");
- hidl_cb(hidl_string());
- return Void();
-}
-
-Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) {
- ALOGI("FakeComposerService::createClient %p", mClient.get());
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_1::Error::NONE, mClient);
- return Void();
-}
-
-FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client)
- : mClient(client) {}
-
-FakeComposerService_2_3::~FakeComposerService_2_3() {
- ALOGI("Maybe killing client %p", mClient.get());
- // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) {
- ALOGI("FakeComposerService::getCapabilities");
- hidl_cb(hidl_vec<Capability>());
- return Void();
-}
-
-Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
- ALOGI("FakeComposerService::dumpDebugInfo");
- hidl_cb(hidl_string());
- return Void();
-}
-
-Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) {
- LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3");
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
- return Void();
-}
-
-Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) {
- ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get());
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_1::Error::NONE, mClient);
- return Void();
-}
-
-FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client)
- : mClient(client) {}
-
-FakeComposerService_2_4::~FakeComposerService_2_4() {
- ALOGI("Maybe killing client %p", mClient.get());
- // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) {
- ALOGI("FakeComposerService::getCapabilities");
- hidl_cb(hidl_vec<Capability>());
- return Void();
-}
-
-Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
- ALOGI("FakeComposerService::dumpDebugInfo");
- hidl_cb(hidl_string());
- return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) {
- LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4");
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
- return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) {
- LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4");
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
- return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) {
- ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get());
- if (!mClient->init()) {
- LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
- }
- hidl_cb(V2_4::Error::NONE, mClient);
- return Void();
-}
-
-} // namespace sftest
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
deleted file mode 100644
index 47f970f..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 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/hardware/graphics/composer/2.4/IComposer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-#include <composer-hal/2.4/ComposerClient.h>
-
-using android::hardware::Return;
-
-using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient;
-
-namespace sftest {
-
-using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer;
-
-class FakeComposerService_2_1 : public IComposer_2_1 {
-public:
- explicit FakeComposerService_2_1(android::sp<ComposerClient>& client);
- virtual ~FakeComposerService_2_1();
-
- Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
- Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
- Return<void> createClient(createClient_cb hidl_cb) override;
-
-private:
- android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer;
-class FakeComposerService_2_2 : public IComposer_2_2 {
-public:
- explicit FakeComposerService_2_2(android::sp<ComposerClient>& client);
- virtual ~FakeComposerService_2_2();
-
- Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
- Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
- Return<void> createClient(createClient_cb hidl_cb) override;
-
-private:
- android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer;
-class FakeComposerService_2_3 : public IComposer_2_3 {
-public:
- explicit FakeComposerService_2_3(android::sp<ComposerClient>& client);
- virtual ~FakeComposerService_2_3();
-
- Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
- Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
- Return<void> createClient(createClient_cb hidl_cb) override;
- Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
-
-private:
- android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer;
-
-class FakeComposerService_2_4 : public IComposer_2_4 {
-public:
- explicit FakeComposerService_2_4(android::sp<ComposerClient>& client);
- virtual ~FakeComposerService_2_4();
-
- Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
- Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
- Return<void> createClient(createClient_cb hidl_cb) override;
- Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
- Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override;
-
-private:
- android::sp<ComposerClient> mClient;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
deleted file mode 100644
index 1cea25a..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright 2017 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcUtil"
-#include <log/log.h>
-
-#include "FakeComposerUtils.h"
-#include "RenderState.h"
-
-#include "SurfaceFlinger.h" // Get the name of the service...
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <hidl/ServiceManagement.h>
-
-#include <iomanip>
-#include <thread>
-
-using android::String16;
-using android::sp;
-using namespace std::chrono_literals;
-using namespace sftest;
-using std::setw;
-
-namespace sftest {
-
-// clang-format off
-inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
- os << std::fixed << std::setprecision(1) << "("
- << setw(align) << sourceRect.left << setw(0) << ","
- << setw(align) << sourceRect.top << setw(0) << ","
- << setw(align) << sourceRect.right << setw(0) << ","
- << setw(align) << sourceRect.bottom << setw(0) << ")";
-}
-
-inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
- os << "("
- << setw(align) << displayRect.left << setw(0) << ","
- << setw(align) << displayRect.top << setw(0) << ","
- << setw(align) << displayRect.right << setw(0) << ","
- << setw(align) << displayRect.bottom << setw(0) << ")";
-}
-// clang-format on
-
-inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
- printSourceRectAligned(os, state.mSourceCrop, 7);
- os << "->";
- printDisplayRectAligned(os, state.mDisplayFrame, 5);
- return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
- << state.mPlaneAlpha << " Xform:" << state.mTransform;
-}
-
-// Helper for verifying the parts of the RenderState
-template <typename T>
-bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
- const char* name) {
- if (ref != val) {
- message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
- return false;
- }
- return true;
-}
-
-::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
- // TODO: Message could start as success and be assigned as failure.
- // Only problem is that utility assumes it to be failure and just adds stuff. Would
- // need still special case the initial failure in the utility?
- // TODO: ... or would it be possible to break this back to gtest primitives?
- ::testing::AssertionResult message = ::testing::AssertionFailure();
- bool passes = true;
-
- // The work here is mostly about providing good log strings for differences
- passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
- passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
- passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
- passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
- // ... add more
- if (passes) {
- return ::testing::AssertionSuccess();
- }
- return message;
-}
-
-::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
- const std::vector<RenderState>& val) {
- ::testing::AssertionResult message = ::testing::AssertionFailure();
- bool passed = true;
- if (ref.size() != val.size()) {
- message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
- passed = false;
- }
- for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
- ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
- if (rectResult == false) {
- message << "First different rect at " << rectIndex << ": " << rectResult.message();
- passed = false;
- break;
- }
- }
-
- if (passed) {
- return ::testing::AssertionSuccess();
- } else {
- message << "\nReference:";
- for (auto state = ref.begin(); state != ref.end(); ++state) {
- message << "\n" << *state;
- }
- message << "\nActual:";
- for (auto state = val.begin(); state != val.end(); ++state) {
- message << "\n" << *state;
- }
- }
- return message;
-}
-
-void startSurfaceFlinger() {
- ALOGI("Start SurfaceFlinger");
- system("start surfaceflinger");
-
- sp<android::IServiceManager> sm(android::defaultServiceManager());
- sp<android::IBinder> sf;
- while (sf == nullptr) {
- std::this_thread::sleep_for(10ms);
- sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
- }
- ALOGV("SurfaceFlinger running");
-}
-
-void stopSurfaceFlinger() {
- ALOGI("Stop SurfaceFlinger");
- system("stop surfaceflinger");
- sp<android::IServiceManager> sm(android::defaultServiceManager());
- sp<android::IBinder> sf;
- while (sf != nullptr) {
- std::this_thread::sleep_for(10ms);
- sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
- }
- ALOGV("SurfaceFlinger stopped");
-}
-
-////////////////////////////////////////////////
-
-void FakeHwcEnvironment::SetUp() {
- ALOGI("Test env setup");
- system("setenforce 0");
- system("stop");
- property_set("debug.sf.nobootanimation", "1");
- {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.sf.nobootanimation", value, "0");
- LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
- }
- // TODO: Try registering the mock as the default service instead.
- property_set("debug.sf.hwc_service_name", "mock");
-
- // This allows tests/SF to register/load a HIDL service not listed in manifest files.
- android::hardware::details::setTrebleTestingOverride(true);
- property_set("debug.sf.treble_testing_override", "true");
-}
-
-void FakeHwcEnvironment::TearDown() {
- ALOGI("Test env tear down");
- system("stop");
- // Wait for mock call signaling teardown?
- property_set("debug.sf.nobootanimation", "0");
- property_set("debug.sf.hwc_service_name", "default");
- system("setenforce 1");
- ALOGI("Test env tear down - done");
-}
-
-} // namespace sftest
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
deleted file mode 100644
index 383a111..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2017 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 "FakeComposerClient.h"
-
-#include <gui/SurfaceComposerClient.h>
-#include <log/log.h>
-#include <gtest/gtest.h>
-
-// clang-format off
-// Note: This needs to reside in the global namespace for the GTest to use it
-inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
- return os << "(" << rect.left << ","
- << rect.top << ","
- << rect.right << ","
- << rect.bottom << ")";
-}
-
-inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
- return os << "(" << rect.left << ","
- << rect.top << ","
- << rect.right << ","
- << rect.bottom << ")";
-}
-// clang-format on
-
-namespace sftest {
-
-class RenderState;
-
-// clang-format off
-inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
- return a.top == b.top &&
- a.left == b.left &&
- a.bottom == b.bottom &&
- a.right == b.right;
-}
-
-inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
- return a.top == b.top &&
- a.left == b.left &&
- a.bottom == b.bottom &&
- a.right == b.right;
-}
-// clang-format on
-
-inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
- return !(a == b);
-}
-
-inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
- return !(a == b);
-}
-
-::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
-::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
- const std::vector<RenderState>& val);
-
-void startSurfaceFlinger();
-void stopSurfaceFlinger();
-
-class FakeHwcEnvironment : public ::testing::Environment {
-public:
- virtual ~FakeHwcEnvironment() {}
- void SetUp() override;
- void TearDown() override;
-};
-
-/*
- * All surface state changes are supposed to happen inside a global
- * transaction. TransactionScope object at the beginning of
- * scope automates the process. The resulting scope gives a visual cue
- * on the span of the transaction as well.
- *
- * Closing the transaction is synchronous, i.e., it waits for
- * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
- * is built to explicitly request vsyncs one at the time. A delayed
- * request must be made before closing the transaction or the test
- * thread stalls until SurfaceFlinger does an emergency vsync by
- * itself. TransactionScope encapsulates this vsync magic.
- */
-class TransactionScope : public android::SurfaceComposerClient::Transaction {
-public:
- explicit TransactionScope(FakeComposerClient& composer) : Transaction(), mComposer(composer) {}
-
- ~TransactionScope() {
- int frameCount = mComposer.getFrameCount();
- mComposer.runVSyncAfter(1ms);
- LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply());
- // Make sure that exactly one frame has been rendered.
- mComposer.waitUntilFrame(frameCount + 1);
- // LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
- // "Unexpected frame advance. Delta: %d",
- // mComposer.getFrameCount() - frameCount);
- }
-
- FakeComposerClient& mComposer;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
deleted file mode 100644
index 5dc3778..0000000
--- a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#pragma once
-
-#include <composer-hal/2.4/ComposerClient.h>
-
-#include <gmock/gmock.h>
-
-using namespace android::hardware::graphics::common;
-using namespace android::hardware::graphics::composer;
-using namespace android::hardware::graphics::composer::V2_4;
-using namespace android::hardware::graphics::composer::V2_4::hal;
-using namespace android::hardware;
-using namespace std::chrono_literals;
-
-namespace sftest {
-
-// Mock class for ComposerHal. Implements only the functions used in the test.
-class MockComposerHal {
-public:
- MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*));
- MOCK_METHOD4(getDisplayAttribute_2_4,
- V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*));
- MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*));
- MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config));
- MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*));
- MOCK_METHOD4(setActiveConfigWithConstraints,
- V2_4::Error(Display, Config,
- const V2_4::IComposerClient::VsyncPeriodChangeConstraints&,
- VsyncPeriodChangeTimeline*));
-};
-
-} // namespace sftest
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
deleted file mode 100644
index 40193f2..0000000
--- a/services/surfaceflinger/tests/fakehwc/RenderState.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 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 <vector>
-
-namespace sftest {
-// Description of a rendered rectangle. Should only contain
-// instructions necessary to rasterize the rectangle. The full scene
-// is given as a sorted list of rectangles, bottom layer at index 0.
-class RenderState {
-public:
- RenderState() = default;
- // Default copy-ctor
-
- hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
- hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
- std::vector<hwc_rect_t> mVisibleRegion;
- hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
- buffer_handle_t mBuffer = 0;
- uint32_t mSwapCount = 0; // How many set buffer calls to the layer.
- int32_t mAcquireFence = 0; // Probably should not be here.
- float mPlaneAlpha = 0.f;
- hwc_color_t mLayerColor = {0, 0, 0, 0};
- hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
deleted file mode 100644
index 1d3401a..0000000
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ /dev/null
@@ -1,1794 +0,0 @@
-/*
- * Copyright (C) 2017 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcTest"
-
-#include "FakeComposerClient.h"
-#include "FakeComposerService.h"
-#include "FakeComposerUtils.h"
-#include "MockComposerHal.h"
-
-#include <binder/Parcel.h>
-#include <gui/AidlStatusUtil.h>
-#include <gui/DisplayEventReceiver.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <android/hidl/manager/1.0/IServiceManager.h>
-#include <android/looper.h>
-#include <android/native_window.h>
-#include <binder/ProcessState.h>
-#include <hwbinder/ProcessState.h>
-#include <log/log.h>
-#include <private/gui/ComposerService.h>
-#include <private/gui/ComposerServiceAIDL.h>
-#include <ui/DisplayMode.h>
-#include <ui/DynamicDisplayInfo.h>
-#include <utils/Looper.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <limits>
-#include <thread>
-
-using namespace std::chrono_literals;
-
-using namespace android;
-using namespace android::hardware;
-
-using namespace sftest;
-
-namespace {
-
-// Mock test helpers
-using ::testing::_;
-using ::testing::DoAll;
-using ::testing::Return;
-using ::testing::SetArgPointee;
-
-using Transaction = SurfaceComposerClient::Transaction;
-using Attribute = V2_4::IComposerClient::Attribute;
-using Display = V2_1::Display;
-
-///////////////////////////////////////////////
-constexpr PhysicalDisplayId physicalIdFromHwcDisplayId(Display hwcId) {
- return PhysicalDisplayId::fromPort(hwcId);
-}
-constexpr PhysicalDisplayId kPrimaryDisplayId = physicalIdFromHwcDisplayId(PRIMARY_DISPLAY);
-constexpr PhysicalDisplayId kExternalDisplayId = physicalIdFromHwcDisplayId(EXTERNAL_DISPLAY);
-
-struct TestColor {
-public:
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-};
-
-constexpr static TestColor RED = {195, 63, 63, 255};
-constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
-constexpr static TestColor GREEN = {63, 195, 63, 255};
-constexpr static TestColor BLUE = {63, 63, 195, 255};
-constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
- bool unlock = true) {
- ANativeWindow_Buffer outBuffer;
- sp<Surface> s = sc->getSurface();
- ASSERT_TRUE(s != nullptr);
- ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
- uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
- for (int y = 0; y < outBuffer.height; y++) {
- for (int x = 0; x < outBuffer.width; x++) {
- uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
- pixel[0] = color.r;
- pixel[1] = color.g;
- pixel[2] = color.b;
- pixel[3] = color.a;
- }
- }
- if (unlock) {
- ASSERT_EQ(NO_ERROR, s->unlockAndPost());
- }
-}
-
-inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
- RenderState res;
- res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
- res.mPlaneAlpha = 1.0f;
- res.mSwapCount = 0;
- res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
- static_cast<float>(bottom - top)};
- return res;
-}
-
-inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
- unsigned int bottom) {
- EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
- EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
- EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
- EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
- return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
- static_cast<int>(bottom));
-}
-
-///////////////////////////////////////////////
-template <typename FakeComposerService>
-class DisplayTest : public ::testing::Test {
-protected:
- struct TestConfig {
- int32_t id;
- int32_t w;
- int32_t h;
- int32_t vsyncPeriod;
- int32_t group;
- };
-
- static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
- auto self = static_cast<DisplayTest*>(data);
-
- ssize_t n;
- DisplayEventReceiver::Event buffer[1];
-
- while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
- for (int i = 0; i < n; i++) {
- self->mReceivedDisplayEvents.push_back(buffer[i]);
- }
- }
- ALOGD_IF(n < 0, "Error reading events (%s)", strerror(-n));
- return 1;
- }
-
- Error getDisplayAttributeNoMock(Display display, Config config,
- V2_4::IComposerClient::Attribute attribute, int32_t* outValue) {
- mFakeComposerClient->setMockHal(nullptr);
- auto ret =
- mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue);
- mFakeComposerClient->setMockHal(mMockComposer.get());
- return ret;
- }
-
- void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
- Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
- std::vector<Config> configIds;
- for (size_t i = 0; i < testConfigs.size(); i++) {
- configIds.push_back(testConfigs[i].id);
-
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE)));
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE)));
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD,
- _))
- .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod),
- Return(Error::NONE)));
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP,
- _))
- .WillRepeatedly(
- DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE)));
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _))
- .WillRepeatedly(Return(Error::UNSUPPORTED));
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _))
- .WillRepeatedly(Return(Error::UNSUPPORTED));
- }
-
- EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)),
- Return(V2_1::Error::NONE)));
-
- EXPECT_CALL(*mMockComposer, getActiveConfig(display, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE)));
-
- EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _))
- .WillRepeatedly(
- DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE)));
- }
-
- void SetUp() override {
- mMockComposer = std::make_unique<MockComposerHal>();
- mFakeComposerClient = new FakeComposerClient();
- mFakeComposerClient->setMockHal(mMockComposer.get());
-
- auto client = sp<V2_4::hal::ComposerClient>::make(mFakeComposerClient);
- mFakeService = sp<FakeComposerService>::make(client);
- ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::ProcessState::self()->startThreadPool();
-
- setExpectationsForConfigs(PRIMARY_DISPLAY,
- {{
- .id = 1,
- .w = 1920,
- .h = 1024,
- .vsyncPeriod = 16'666'666,
- .group = 0,
- }},
- 1, 16'666'666);
-
- startSurfaceFlinger();
-
- // Fake composer wants to enable VSync injection
- mFakeComposerClient->onSurfaceFlingerStart();
-
- mComposerClient = sp<SurfaceComposerClient>::make();
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mReceiver.reset(
- new DisplayEventReceiver(gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- gui::ISurfaceComposer::EventRegistration::modeChanged));
- mLooper = sp<Looper>::make(false);
- mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
- }
-
- void TearDown() override {
- mLooper = nullptr;
- mReceiver = nullptr;
-
- mComposerClient->dispose();
- mComposerClient = nullptr;
-
- // Fake composer needs to release SurfaceComposerClient before the stop.
- mFakeComposerClient->onSurfaceFlingerStop();
- stopSurfaceFlinger();
-
- mFakeComposerClient->setMockHal(nullptr);
-
- mFakeService = nullptr;
- // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
- // management.
- mMockComposer = nullptr;
- }
-
- void waitForDisplayTransaction(Display display) {
- // Both a refresh and a vsync event are needed to apply pending display
- // transactions.
- mFakeComposerClient->refreshDisplay(display);
- mFakeComposerClient->runVSyncAndWait();
-
- // Extra vsync and wait to avoid a 10% flake due to a race.
- mFakeComposerClient->runVSyncAndWait();
- }
-
- bool waitForHotplugEvent(Display displayId, bool connected) {
- return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
- }
-
- bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
- int waitCount = 20;
- while (waitCount--) {
- while (!mReceivedDisplayEvents.empty()) {
- auto event = mReceivedDisplayEvents.front();
- mReceivedDisplayEvents.pop_front();
-
- ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
- "event hotplug: displayId %s, connected %d",
- to_string(event.header.displayId).c_str(), event.hotplug.connected);
-
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
- event.header.displayId == displayId && event.hotplug.connected == connected) {
- return true;
- }
- }
-
- mLooper->pollOnce(1);
- }
- return false;
- }
-
- bool waitForModeChangedEvent(Display display, int32_t modeId) {
- PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
- int waitCount = 20;
- while (waitCount--) {
- while (!mReceivedDisplayEvents.empty()) {
- auto event = mReceivedDisplayEvents.front();
- mReceivedDisplayEvents.pop_front();
-
- ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
- "event mode: displayId %s, modeId %d",
- to_string(event.header.displayId).c_str(), event.modeChange.modeId);
-
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE &&
- event.header.displayId == displayId && event.modeChange.modeId == modeId) {
- return true;
- }
- }
-
- mLooper->pollOnce(1);
- }
- return false;
- }
-
- void Test_HotplugOneConfig() {
- ALOGD("DisplayTest::Test_Hotplug_oneConfig");
-
- setExpectationsForConfigs(EXTERNAL_DISPLAY,
- {{.id = 1,
- .w = 200,
- .h = 400,
- .vsyncPeriod = 16'666'666,
- .group = 0}},
- 1, 16'666'666);
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
- EXPECT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- const ui::Size& resolution = mode.resolution;
- EXPECT_EQ(ui::Size(200, 400), resolution);
- EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- mFakeComposerClient->clearFrames();
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
- EXPECT_TRUE(display == nullptr);
-
- ui::DisplayMode mode;
- EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- }
- }
-
- void Test_HotplugTwoSeparateConfigs() {
- ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs");
-
- setExpectationsForConfigs(EXTERNAL_DISPLAY,
- {{.id = 1,
- .w = 200,
- .h = 400,
- .vsyncPeriod = 16'666'666,
- .group = 0},
- {.id = 2,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 11'111'111,
- .group = 1}},
- 1, 16'666'666);
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
- EXPECT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(200, 400), mode.resolution);
- EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- ui::DynamicDisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
- const auto& modes = info.supportedDisplayModes;
- EXPECT_EQ(modes.size(), 2);
-
- // change active mode
-
- if (mIs2_4Client) {
- EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
- .WillOnce(Return(V2_4::Error::NONE));
- } else {
- EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2))
- .WillOnce(Return(V2_1::Error::NONE));
- }
-
- for (int i = 0; i < modes.size(); i++) {
- const auto& mode = modes[i];
- if (mode.resolution.getWidth() == 800) {
- EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate));
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
- break;
- }
- }
-
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- mFakeComposerClient->clearFrames();
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
- }
-
- void Test_HotplugTwoConfigsSameGroup() {
- ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup");
-
- setExpectationsForConfigs(EXTERNAL_DISPLAY,
- {{.id = 2,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 16'666'666,
- .group = 31},
- {.id = 3,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 11'111'111,
- .group = 31}},
- 2, 16'666'666);
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
- EXPECT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- ui::DynamicDisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
- const auto& modes = info.supportedDisplayModes;
- EXPECT_EQ(modes.size(), 2);
-
- // change active mode
- if (mIs2_4Client) {
- EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
- .WillOnce(Return(V2_4::Error::NONE));
- } else {
- EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
- .WillOnce(Return(V2_1::Error::NONE));
- }
-
- for (int i = 0; i < modes.size(); i++) {
- const auto& mode = modes[i];
- if (mode.refreshRate == 1e9f / 11'111'111) {
- EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate));
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
- break;
- }
- }
-
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- mFakeComposerClient->clearFrames();
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
- }
-
- void Test_HotplugThreeConfigsMixedGroups() {
- ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups");
-
- setExpectationsForConfigs(EXTERNAL_DISPLAY,
- {{.id = 2,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 16'666'666,
- .group = 0},
- {.id = 3,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 11'111'111,
- .group = 0},
- {.id = 4,
- .w = 1600,
- .h = 3200,
- .vsyncPeriod = 8'333'333,
- .group = 1},
- {.id = 5,
- .w = 1600,
- .h = 3200,
- .vsyncPeriod = 11'111'111,
- .group = 1}},
- 2, 16'666'666);
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
- EXPECT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- ui::DynamicDisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
- const auto& modes = info.supportedDisplayModes;
- EXPECT_EQ(modes.size(), 4);
-
- // change active mode to 800x1600@90Hz
- if (mIs2_4Client) {
- EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
- .WillOnce(Return(V2_4::Error::NONE));
- } else {
- EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
- .WillOnce(Return(V2_1::Error::NONE));
- }
-
- for (size_t i = 0; i < modes.size(); i++) {
- const auto& mode = modes[i];
- if (mode.resolution.getWidth() == 800 && mode.refreshRate == 1e9f / 11'111'111) {
- EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
- modes[i].refreshRate,
- modes[i].refreshRate,
- modes[i].refreshRate,
- modes[i].refreshRate));
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
- break;
- }
- }
-
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- // change active mode to 1600x3200@120Hz
- if (mIs2_4Client) {
- EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
- .WillOnce(Return(V2_4::Error::NONE));
- } else {
- EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4))
- .WillOnce(Return(V2_1::Error::NONE));
- }
-
- for (int i = 0; i < modes.size(); i++) {
- const auto& mode = modes[i];
- if (mode.refreshRate == 1e9f / 8'333'333) {
- EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate));
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
- break;
- }
- }
-
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
- EXPECT_EQ(1e9f / 8'333'333, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- // change active mode to 1600x3200@90Hz
- if (mIs2_4Client) {
- EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
- .WillOnce(Return(V2_4::Error::NONE));
- } else {
- EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5))
- .WillOnce(Return(V2_1::Error::NONE));
- }
-
- for (int i = 0; i < modes.size(); i++) {
- const auto& mode = modes[i];
- if (mode.resolution.getWidth() == 1600 && mode.refreshRate == 1e9f / 11'111'111) {
- EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate,
- mode.refreshRate));
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
- break;
- }
- }
-
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
- EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
- mFakeComposerClient->clearFrames();
- {
- const ui::Size& resolution = mode.resolution;
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"),
- resolution.getWidth(), resolution.getHeight(),
- PIXEL_FORMAT_RGBA_8888, 0);
- EXPECT_TRUE(surfaceControl != nullptr);
- EXPECT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
-
- {
- TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
- }
- }
-
- mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
- V2_1::IComposerCallback::Connection::DISCONNECTED);
- waitForDisplayTransaction(EXTERNAL_DISPLAY);
- mFakeComposerClient->clearFrames();
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
- }
-
- void Test_HotplugPrimaryDisplay() {
- ALOGD("DisplayTest::HotplugPrimaryDisplay");
-
- mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
- V2_1::IComposerCallback::Connection::DISCONNECTED);
-
- waitForDisplayTransaction(PRIMARY_DISPLAY);
-
- EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
- EXPECT_TRUE(display == nullptr);
-
- ui::DisplayMode mode;
- auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
- EXPECT_NE(NO_ERROR, result);
- }
-
- mFakeComposerClient->clearFrames();
-
- setExpectationsForConfigs(PRIMARY_DISPLAY,
- {{.id = 1,
- .w = 400,
- .h = 200,
- .vsyncPeriod = 16'666'666,
- .group = 0}},
- 1, 16'666'666);
-
- mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
- V2_1::IComposerCallback::Connection::CONNECTED);
-
- waitForDisplayTransaction(PRIMARY_DISPLAY);
-
- EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
-
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
- EXPECT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
- EXPECT_EQ(NO_ERROR, result);
- ASSERT_EQ(ui::Size(400, 200), mode.resolution);
- EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
- }
- }
-
- void Test_SubsequentHotplugConnectUpdatesDisplay(Display hwcDisplayId) {
- ALOGD("DisplayTest::Test_SubsequentHotplugConnectUpdatesDisplay");
-
- // Send a hotplug connected event to set up the initial display modes.
- // The primary display is already connected so this will update it.
- // If we're running the test of an external display this will create it.
- setExpectationsForConfigs(hwcDisplayId,
- {{.id = 1,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 11'111'111,
- .group = 1}},
- /* activeConfig */ 1, 11'111'111);
-
- mFakeComposerClient->hotplugDisplay(hwcDisplayId,
- V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction(hwcDisplayId);
- EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
-
- const auto displayId = physicalIdFromHwcDisplayId(hwcDisplayId);
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(displayId);
- EXPECT_FALSE(display == nullptr);
-
- // Verify that the active mode and the supported moded are updated
- {
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
- ui::DynamicDisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
- const auto& modes = info.supportedDisplayModes;
- EXPECT_EQ(modes.size(), 1);
- }
-
- // Send another hotplug connected event
- setExpectationsForConfigs(hwcDisplayId,
- {
- {.id = 1,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 16'666'666,
- .group = 1},
- {.id = 2,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 11'111'111,
- .group = 1},
- {.id = 3,
- .w = 800,
- .h = 1600,
- .vsyncPeriod = 8'333'333,
- .group = 1},
- },
- /* activeConfig */ 1, 16'666'666);
-
- mFakeComposerClient->hotplugDisplay(hwcDisplayId,
- V2_1::IComposerCallback::Connection::CONNECTED);
- waitForDisplayTransaction(hwcDisplayId);
- EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
-
- // Verify that the active mode and the supported moded are updated
- {
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
- }
-
- ui::DynamicDisplayInfo info;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
- const auto& modes = info.supportedDisplayModes;
- EXPECT_EQ(modes.size(), 3);
-
- EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution);
- EXPECT_EQ(1e9f / 16'666'666, modes[0].refreshRate);
-
- EXPECT_EQ(ui::Size(800, 1600), modes[1].resolution);
- EXPECT_EQ(1e9f / 11'111'111, modes[1].refreshRate);
-
- EXPECT_EQ(ui::Size(800, 1600), modes[2].resolution);
- EXPECT_EQ(1e9f / 8'333'333, modes[2].refreshRate);
-
- // Verify that we are able to switch to any of the modes
- for (int i = modes.size() - 1; i >= 0; i--) {
- const auto hwcId = i + 1;
- // Set up HWC expectations for the mode change
- if (mIs2_4Client) {
- EXPECT_CALL(*mMockComposer,
- setActiveConfigWithConstraints(hwcDisplayId, hwcId, _, _))
- .WillOnce(Return(V2_4::Error::NONE));
- } else {
- EXPECT_CALL(*mMockComposer, setActiveConfig(hwcDisplayId, hwcId))
- .WillOnce(Return(V2_1::Error::NONE));
- }
-
- EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
- modes[i].refreshRate,
- modes[i].refreshRate,
- modes[i].refreshRate,
- modes[i].refreshRate));
- // We need to refresh twice - once to apply the pending mode change request,
- // and once to process the change.
- waitForDisplayTransaction(hwcDisplayId);
- waitForDisplayTransaction(hwcDisplayId);
- EXPECT_TRUE(waitForModeChangedEvent(hwcDisplayId, i))
- << "Failure while switching to mode " << i;
-
- ui::DisplayMode mode;
- EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
- EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
- EXPECT_EQ(modes[i].refreshRate, mode.refreshRate);
- }
- }
-
- sp<V2_1::IComposer> mFakeService;
- sp<SurfaceComposerClient> mComposerClient;
-
- std::unique_ptr<MockComposerHal> mMockComposer;
- FakeComposerClient* mFakeComposerClient;
-
- std::unique_ptr<DisplayEventReceiver> mReceiver;
- sp<Looper> mLooper;
- std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
-
- static constexpr bool mIs2_4Client =
- std::is_same<FakeComposerService, FakeComposerService_2_4>::value;
-};
-
-using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
-
-// Tests that VSYNC injection can be safely toggled while invalidating.
-TEST_F(DisplayTest_2_1, VsyncInjection) {
- const auto flinger = ComposerServiceAIDL::getComposerService();
- bool enable = true;
-
- for (int i = 0; i < 100; i++) {
- flinger->enableVSyncInjections(enable);
- enable = !enable;
-
- constexpr uint32_t kForceInvalidate = 1004;
- android::Parcel data, reply;
- data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
- EXPECT_EQ(NO_ERROR,
- android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
-
- std::this_thread::sleep_for(5ms);
- }
-}
-
-TEST_F(DisplayTest_2_1, HotplugOneConfig) {
- Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) {
- Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) {
- Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) {
- Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) {
- Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesExternalDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
-
-TEST_F(DisplayTest_2_2, HotplugOneConfig) {
- Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) {
- Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) {
- Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) {
- Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) {
- Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesExternalDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
-
-TEST_F(DisplayTest_2_3, HotplugOneConfig) {
- Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) {
- Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) {
- Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) {
- Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) {
- Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesExternalDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
-
-TEST_F(DisplayTest_2_4, HotplugOneConfig) {
- Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) {
- Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) {
- Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) {
- Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) {
- Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesExternalDisplay) {
- Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-////////////////////////////////////////////////
-
-template <typename FakeComposerService>
-class TransactionTest : public ::testing::Test {
-protected:
- // Layer array indexing constants.
- constexpr static int BG_LAYER = 0;
- constexpr static int FG_LAYER = 1;
-
- static void SetUpTestCase() {
- // TODO: See TODO comment at DisplayTest::SetUp for background on
- // the lifetime of the FakeComposerClient.
- sFakeComposer = new FakeComposerClient;
- auto client = sp<V2_4::hal::ComposerClient>::make(sFakeComposer);
- sp<V2_1::IComposer> fakeService = sp<FakeComposerService>::make(client);
- (void)fakeService->registerAsService("mock");
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::ProcessState::self()->startThreadPool();
-
- startSurfaceFlinger();
-
- // Fake composer wants to enable VSync injection
- sFakeComposer->onSurfaceFlingerStart();
- }
-
- static void TearDownTestCase() {
- // Fake composer needs to release SurfaceComposerClient before the stop.
- sFakeComposer->onSurfaceFlingerStop();
- stopSurfaceFlinger();
- // TODO: This is deleted when the ComposerClient calls
- // removeClient. Devise better lifetime control.
- sFakeComposer = nullptr;
- }
-
- void SetUp() override {
- ALOGI("TransactionTest::SetUp");
- mComposerClient = sp<SurfaceComposerClient>::make();
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- ALOGI("TransactionTest::SetUp - display");
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
- ASSERT_FALSE(display == nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
- const ui::Size& resolution = mode.resolution;
- mDisplayWidth = resolution.getWidth();
- mDisplayHeight = resolution.getHeight();
-
- // Background surface
- mBGSurfaceControl =
- mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
- mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mBGSurfaceControl != nullptr);
- ASSERT_TRUE(mBGSurfaceControl->isValid());
- fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
-
- // Foreground surface
- mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mFGSurfaceControl != nullptr);
- ASSERT_TRUE(mFGSurfaceControl->isValid());
-
- fillSurfaceRGBA8(mFGSurfaceControl, RED);
-
- Transaction t;
- t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
- t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
- t.show(mBGSurfaceControl);
-
- t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
- t.setPosition(mFGSurfaceControl, 64, 64);
- t.show(mFGSurfaceControl);
-
- // Synchronous transaction will stop this thread, so we set up a
- // delayed, off-thread vsync request before closing the
- // transaction. In the test code this is usually done with
- // TransactionScope. Leaving here in the 'vanilla' form for
- // reference.
- ASSERT_EQ(0, sFakeComposer->getFrameCount());
- sFakeComposer->runVSyncAfter(1ms);
- t.apply();
- sFakeComposer->waitUntilFrame(1);
-
- // Reference data. This is what the HWC should see.
- static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
- mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
- mBaseFrame[BG_LAYER].mSwapCount = 1;
- mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
- mBaseFrame[FG_LAYER].mSwapCount = 1;
-
- auto frame = sFakeComposer->getFrameRects(0);
- ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
- }
-
- void TearDown() override {
- ALOGD("TransactionTest::TearDown");
-
- mComposerClient->dispose();
- mBGSurfaceControl = 0;
- mFGSurfaceControl = 0;
- mComposerClient = 0;
-
- sFakeComposer->runVSyncAndWait();
- mBaseFrame.clear();
- sFakeComposer->clearFrames();
- ASSERT_EQ(0, sFakeComposer->getFrameCount());
-
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- std::vector<gui::LayerDebugInfo> layers;
- binder::Status status = sf->getLayerDebugInfo(&layers);
- status_t result = gui::aidl_utils::statusTFromBinderStatus(status);
- if (result != NO_ERROR) {
- ALOGE("Failed to get layers %s %d", strerror(-result), result);
- } else {
- // If this fails, the test being torn down leaked layers.
- EXPECT_EQ(0u, layers.size());
- if (layers.size() > 0) {
- for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
- std::cout << to_string(*layer).c_str();
- }
- // To ensure the next test has clean slate, will run the class
- // tear down and setup here.
- TearDownTestCase();
- SetUpTestCase();
- }
- }
- ALOGD("TransactionTest::TearDown - complete");
- }
-
- void Test_LayerMove() {
- ALOGD("TransactionTest::LayerMove");
-
- // The scope opens and closes a global transaction and, at the
- // same time, makes sure the SurfaceFlinger progresses one frame
- // after the transaction closes. The results of the transaction
- // should be available in the latest frame stored by the fake
- // composer.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 128, 128);
- // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
- // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
- //
- // sFakeComposer->runVSyncAndWait();
- }
-
- fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
- sFakeComposer->runVSyncAndWait();
-
- ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
- // there's no extra frames.
-
- // NOTE: Frame 0 is produced in the SetUp.
- auto frame1Ref = mBaseFrame;
- frame1Ref[FG_LAYER].mDisplayFrame =
- hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
- EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
- auto frame2Ref = frame1Ref;
- frame2Ref[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
- }
-
- void Test_LayerCrop() {
- // TODO: Add scaling to confirm that crop happens in buffer space?
- {
- TransactionScope ts(*sFakeComposer);
- Rect cropRect(16, 16, 32, 32);
- ts.setCrop(mFGSurfaceControl, cropRect);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerSetLayer() {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- // The layers will switch order, but both are rendered because the background layer is
- // transparent (RGBA8888).
- std::vector<RenderState> referenceFrame(2);
- referenceFrame[0] = mBaseFrame[FG_LAYER];
- referenceFrame[1] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerSetLayerOpaque() {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
- ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
- layer_state_t::eLayerOpaque);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- // The former foreground layer is now covered with opaque layer - it should have disappeared
- std::vector<RenderState> referenceFrame(1);
- referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_SetLayerStack() {
- ALOGD("TransactionTest::SetLayerStack");
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1});
- }
-
- // Foreground layer should have disappeared.
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerShowHide() {
- ALOGD("TransactionTest::LayerShowHide");
- {
- TransactionScope ts(*sFakeComposer);
- ts.hide(mFGSurfaceControl);
- }
-
- // Foreground layer should have disappeared.
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mFGSurfaceControl);
- }
-
- // Foreground layer should be back
- ASSERT_EQ(3, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerSetAlpha() {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.75f);
- }
-
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerSetFlags() {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
- layer_state_t::eLayerHidden);
- }
-
- // Foreground layer should have disappeared.
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerSetMatrix() {
- struct matrixTestData {
- float matrix[4];
- hwc_transform_t expectedTransform;
- hwc_rect_t expectedDisplayFrame;
- };
-
- // The matrix operates on the display frame and is applied before
- // the position is added. So, the foreground layer rect is (0, 0,
- // 64, 64) is first transformed, potentially yielding negative
- // coordinates and then the position (64, 64) is added yielding
- // the final on-screen rectangles given.
-
- const matrixTestData MATRIX_TESTS[7] = // clang-format off
- {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}},
- {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}},
- {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}},
- {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}},
- {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}},
- {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
- {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
- // clang-format on
- constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
-
- for (int i = 0; i < TEST_COUNT; i++) {
- // TODO: How to leverage the HWC2 stringifiers?
- const matrixTestData& xform = MATRIX_TESTS[i];
- SCOPED_TRACE(i);
- {
- TransactionScope ts(*sFakeComposer);
- ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2],
- xform.matrix[3]);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
- referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
-
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
- }
-
- void Test_SetRelativeLayer() {
- constexpr int RELATIVE_LAYER = 2;
- auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
- 64, PIXEL_FORMAT_RGBA_8888, 0);
- fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
-
- // Now we stack the surface above the foreground surface and make sure it is visible.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(relativeSurfaceControl, 64, 64);
- ts.show(relativeSurfaceControl);
- ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1);
- }
- auto referenceFrame = mBaseFrame;
- // NOTE: All three layers will be visible as the surfaces are
- // transparent because of the RGBA format.
- referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
- referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // A call to setLayer will override a call to setRelativeLayer
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(relativeSurfaceControl, 0);
- }
-
- // Previous top layer will now appear at the bottom.
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
- EXPECT_EQ(3, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
- }
-
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mBGSurfaceControl;
- sp<SurfaceControl> mFGSurfaceControl;
- std::vector<RenderState> mBaseFrame;
- uint32_t mDisplayWidth;
- uint32_t mDisplayHeight;
-
- static inline FakeComposerClient* sFakeComposer;
-};
-
-using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>;
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerMove) {
- Test_LayerMove();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
- Test_LayerCrop();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) {
- Test_LayerSetLayer();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) {
- Test_LayerSetLayerOpaque();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) {
- Test_SetLayerStack();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) {
- Test_LayerShowHide();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) {
- Test_LayerSetAlpha();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) {
- Test_LayerSetFlags();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) {
- Test_LayerSetMatrix();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
- Test_SetRelativeLayer();
-}
-
-template <typename FakeComposerService>
-class ChildLayerTest : public TransactionTest<FakeComposerService> {
- using Base = TransactionTest<FakeComposerService>;
-
-protected:
- constexpr static int CHILD_LAYER = 2;
-
- void SetUp() override {
- Base::SetUp();
- mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0,
- Base::mFGSurfaceControl->getHandle());
- fillSurfaceRGBA8(mChild, LIGHT_GRAY);
-
- Base::sFakeComposer->runVSyncAndWait();
- Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
- Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1;
- ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
- ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void TearDown() override {
- mChild = 0;
- Base::TearDown();
- }
-
- void Test_Positioning() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- // Move to the same position as in the original setup.
- ts.setPosition(Base::mFGSurfaceControl, 64, 64);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- }
-
- auto referenceFrame2 = Base::mBaseFrame;
- referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
- referenceFrame2[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_Cropping() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
- }
- // NOTE: The foreground surface would be occluded by the child
- // now, but is included in the stack because the child is
- // transparent.
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
- referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_Constraints() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setPosition(mChild, 63, 63);
- }
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_Scaling() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- }
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0);
- }
-
- auto referenceFrame2 = Base::mBaseFrame;
- referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
- referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerAlpha() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setAlpha(mChild, 0.5);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setAlpha(Base::mFGSurfaceControl, 0.5);
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
- referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
- sp<SurfaceControl> mChild;
-};
-
-using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>;
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) {
- Test_Positioning();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Cropping) {
- Test_Cropping();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) {
- Test_Constraints();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) {
- Test_Scaling();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_LayerAlpha) {
- Test_LayerAlpha();
-}
-
-template <typename FakeComposerService>
-class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
- using Base = ChildLayerTest<FakeComposerService>;
-
-protected:
- void SetUp() override {
- Base::SetUp();
- Base::mChild =
- Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect,
- Base::mFGSurfaceControl->getHandle());
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setColor(Base::mChild,
- {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
- ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
- }
-
- Base::sFakeComposer->runVSyncAndWait();
- Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
- Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
- Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0;
- ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
- ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerAlpha() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(Base::mChild);
- ts.setPosition(Base::mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setAlpha(Base::mChild, 0.5);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setAlpha(Base::mFGSurfaceControl, 0.5);
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
- referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f;
- EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
- }
-
- void Test_LayerZeroAlpha() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(Base::mChild);
- ts.setPosition(Base::mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- ts.setAlpha(Base::mChild, 0.5);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setAlpha(Base::mFGSurfaceControl, 0.0f);
- }
-
- std::vector<RenderState> refFrame(1);
- refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
-
- EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
- }
-};
-
-using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>;
-
-TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerAlpha) {
- Test_LayerAlpha();
-}
-
-TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
- Test_LayerZeroAlpha();
-}
-} // namespace
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
-
- auto* fakeEnvironment = new sftest::FakeHwcEnvironment;
- ::testing::AddGlobalTestEnvironment(fakeEnvironment);
- ::testing::InitGoogleMock(&argc, argv);
- return RUN_ALL_TESTS();
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 4469df0..bf22521 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -34,7 +34,6 @@
"mock/MockFrameTimeline.cpp",
"mock/MockFrameTracer.cpp",
"mock/MockNativeWindowSurface.cpp",
- "mock/MockSurfaceInterceptor.cpp",
"mock/MockTimeStats.cpp",
"mock/MockVsyncController.cpp",
"mock/MockVSyncTracker.cpp",
@@ -71,7 +70,6 @@
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
"AidlPowerHalWrapperTest.cpp",
- "CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
"DisplayIdGeneratorTest.cpp",
@@ -86,6 +84,7 @@
"FpsTest.cpp",
"FramebufferSurfaceTest.cpp",
"FrameRateOverrideMappingsTest.cpp",
+ "FrameRateSelectionPriorityTest.cpp",
"FrameTimelineTest.cpp",
"GameModeTest.cpp",
"HWComposerTest.cpp",
@@ -93,6 +92,8 @@
"LayerHistoryTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
+ "LayerHierarchyTest.cpp",
+ "LayerLifecycleManagerTest.cpp",
"LayerTest.cpp",
"LayerTestUtils.cpp",
"MessageQueueTest.cpp",
@@ -110,10 +111,10 @@
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SchedulerTest.cpp",
"SetFrameRateTest.cpp",
- "RefreshRateConfigsTest.cpp",
- "RefreshRateSelectionTest.cpp",
+ "RefreshRateSelectorTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
"TimeStatsTest.cpp",
@@ -151,7 +152,7 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V4-cpp",
"libaidlcommonsupport",
"libcompositionengine_mocks",
"libcompositionengine",
@@ -168,7 +169,6 @@
"libtimestats_atoms_proto",
"libtimestats_proto",
"libtonemap",
- "libtrace_proto",
"perfetto_trace_protos",
],
shared_libs: [
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
deleted file mode 100644
index c1cbbfb..0000000
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CachingTest"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-class SlotGenerationTest : public testing::Test {
-protected:
- sp<HwcSlotGenerator> mHwcSlotGenerator = sp<HwcSlotGenerator>::make();
- sp<GraphicBuffer> mBuffer1 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer2 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer3 =
- sp<GraphicBuffer>::make(10u, 10u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
-};
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) {
- sp<IBinder> binder = sp<BBinder>::make();
- // test getting invalid client_cache_id
- client_cache_t id;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) {
- sp<IBinder> binder = sp<BBinder>::make();
- client_cache_t id;
- id.token = binder;
- id.id = 0;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-
- client_cache_t idB;
- idB.token = binder;
- idB.id = 1;
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) {
- sp<IBinder> binder = sp<BBinder>::make();
- std::vector<client_cache_t> ids;
- uint32_t cacheId = 0;
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- ids.push_back(id);
-
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- }
-
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 7148c11..06b9caa 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -244,8 +244,8 @@
HAL_PIXEL_FORMAT_RGBA_8888, 1,
usage);
- auto future = mFlinger.renderScreenImpl(*renderArea, traverseLayers, mCaptureScreenBuffer,
- forSystem, regionSampling);
+ auto future = mFlinger.renderScreenImpl(std::move(renderArea), traverseLayers,
+ mCaptureScreenBuffer, forSystem, regionSampling);
ASSERT_TRUE(future.valid());
const auto fenceResult = future.get();
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 982b9ff..60ad7a3 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include "DisplayTransactionTestHelpers.h"
+#include "mock/MockFrameRateMode.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -29,6 +30,7 @@
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
+ using Action = DisplayDevice::DesiredActiveModeAction;
using Event = scheduler::DisplayModeEvent;
void SetUp() override {
@@ -56,31 +58,42 @@
static constexpr DisplayModeId kModeId90{1};
static constexpr DisplayModeId kModeId120{2};
- static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
- static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
- static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
+ static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+ ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+ ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
};
TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
- EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None}));
+ EXPECT_EQ(Action::None,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None}));
EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
}
TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
- // Setting another mode should be cached but return false
- EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+ // 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_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
}
TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
mDisplay->clearDesiredActiveModeState();
@@ -88,9 +101,11 @@
}
TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -101,18 +116,27 @@
EXPECT_EQ(OK,
mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
&timeline));
- EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
mDisplay->clearDesiredActiveModeState();
ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
}
+TEST_F(InitiateModeChangeTest, initiateRenderRateChange) {
+ EXPECT_EQ(Action::InitiateRenderRateSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None}));
+ EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
NO_THREAD_SAFETY_ANALYSIS {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -123,21 +147,23 @@
EXPECT_EQ(OK,
mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
&timeline));
- EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
- EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+ EXPECT_EQ(Action::None,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
- EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
EXPECT_EQ(OK,
mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
&timeline));
- EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
mDisplay->clearDesiredActiveModeState();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 31262b3..e0b508a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -36,8 +36,7 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- // Default to no wide color display support configured
- mFlinger.mutableHasWideColorDisplay() = false;
+ mFlinger.mutableSupportsWideColor() = false;
mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) {
@@ -51,7 +50,6 @@
injectMockScheduler();
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
- mFlinger.mutableInterceptor() = mSurfaceInterceptor;
injectMockComposer(0);
}
@@ -122,51 +120,6 @@
});
}
-sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
- std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
- constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
- constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
- constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
-
- // The DisplayDevice is required to have a framebuffer (behind the
- // ANativeWindow interface) which uses the actual hardware display
- // size.
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
- compositionengine::DisplayCreationArgsBuilder()
- .setId(DEFAULT_DISPLAY_ID)
- .setPixels({DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT})
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
-
- constexpr bool kIsPrimary = true;
- auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
- ui::DisplayConnectionType::Internal,
- DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
-
- injector.setNativeWindow(mNativeWindow);
- if (injectExtra) {
- injectExtra(injector);
- }
-
- auto displayDevice = injector.inject();
-
- Mock::VerifyAndClear(mNativeWindow.get());
-
- return displayDevice;
-}
-
bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const {
const auto& map = mFlinger.hwcPhysicalDisplayIdMap();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 885d6cd..d58e644 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -42,6 +42,7 @@
#include <renderengine/mock/RenderEngine.h>
#include <ui/DebugUtils.h>
+#include "FakeDisplayInjector.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -49,7 +50,6 @@
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
#include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSurfaceInterceptor.h"
#include "mock/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
@@ -89,8 +89,11 @@
void injectMockComposer(int virtualDisplayCount);
void injectFakeBufferQueueFactory();
void injectFakeNativeWindowSurfaceFactory();
+
sp<DisplayDevice> injectDefaultInternalDisplay(
- std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+ std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)> injectExtra) {
+ return mFakeDisplayInjector.injectInternalDisplay(injectExtra);
+ }
// --------------------------------------------------------------------
// Postcondition helpers
@@ -115,12 +118,13 @@
sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make();
Hwc2::mock::PowerAdvisor mPowerAdvisor;
+ FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
+
// These mocks are created by the test, but are destroyed by SurfaceFlinger
// by virtue of being stored into a std::unique_ptr. However we still need
// to keep a reference to them for use in setting up call expectations.
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
Hwc2::mock::Composer* mComposer = nullptr;
- sp<mock::SurfaceInterceptor> mSurfaceInterceptor = sp<mock::SurfaceInterceptor>::make();
mock::VsyncController* mVsyncController = new mock::VsyncController;
mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
@@ -355,6 +359,7 @@
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
const auto displayId = DisplayVariant::DISPLAY_ID::get();
ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -363,18 +368,21 @@
.setHwcDisplayId(HWC_DISPLAY_ID)
.setResolution(DisplayVariant::RESOLUTION)
.setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
+ .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
.inject(&test->mFlinger, test->mComposer);
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplay(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
- .WillOnce(Return(Error::NONE));
- injectHwcDisplayWithNoDefaultCapabilities(test);
+ if constexpr (kInitPowerMode) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ .WillOnce(Return(Error::NONE));
+ }
+ injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
}
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -679,12 +687,10 @@
static constexpr bool WIDE_COLOR_SUPPORTED = false;
static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableHasWideColorDisplay() = true;
+ test->mFlinger.mutableSupportsWideColor() = true;
}
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
}
};
@@ -696,12 +702,11 @@
static constexpr bool WIDE_COLOR_SUPPORTED = false;
static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableHasWideColorDisplay() = false;
+ test->mFlinger.mutableSupportsWideColor() = false;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
}
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 978afc5..dd87f9d 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -93,7 +93,6 @@
void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
std::chrono::nanoseconds expectedReadyDuration);
VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
- void expectInterceptCallReceived(nsecs_t expectedTimestamp);
void expectVsyncEventReceivedByConnection(const char* name,
ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount);
@@ -114,7 +113,6 @@
AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
mVSyncSetDurationCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
- AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
@@ -181,7 +179,6 @@
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
- mInterceptVSyncCallRecorder.getInvocable(),
throttleVsync, getVsyncPeriod);
// EventThread should register itself as VSyncSource callback.
@@ -219,12 +216,6 @@
return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
}
-void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) {
- auto args = mInterceptVSyncCallRecorder.waitForCall();
- ASSERT_TRUE(args.has_value());
- EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
-}
-
void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
auto args = mThrottleVsyncCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
@@ -348,7 +339,6 @@
EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
- EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
}
@@ -374,17 +364,15 @@
expectVSyncSetEnabledCallReceived(true);
// Use the received callback to signal a first vsync event.
- // The interceptor should receive the event, as well as the connection.
+ // The throttler should receive the event, as well as the connection.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
- // The interceptor should receive the event, but the connection should
+ // The throttler should receive the event, but the connection should
// not as it was only interested in the first.
mCallback->onVSyncEvent(456, {123, 0});
- expectInterceptCallReceived(456);
EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -400,10 +388,9 @@
expectVSyncSetEnabledCallReceived(true);
// Use the received callback to signal a vsync event.
- // The interceptor should receive the event, as well as the connection.
+ // The throttler should receive the event, as well as the connection.
VSyncSource::VSyncData vsyncData = {456, 789};
mCallback->onVSyncEvent(123, vsyncData);
- expectInterceptCallReceived(123);
expectVsyncEventFrameTimelinesCorrect(123, vsyncData);
}
@@ -477,10 +464,9 @@
expectVSyncSetEnabledCallReceived(true);
// Send a vsync event. EventThread should then make a call to the
- // interceptor, and the second connection. The first connection should not
+ // the second connection. The first connection should not
// get the event.
mCallback->onVSyncEvent(123, {456, 0});
- expectInterceptCallReceived(123);
EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
1u);
@@ -493,21 +479,18 @@
expectVSyncSetEnabledCallReceived(true);
// Send a vsync event. EventThread should then make a call to the
- // interceptor, and the connection.
+ // throttler, and the connection.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
mCallback->onVSyncEvent(456, {123, 0});
- expectInterceptCallReceived(456);
expectThrottleVsyncReceived(123, mConnectionUid);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
mCallback->onVSyncEvent(789, {777, 111});
- expectInterceptCallReceived(789);
expectThrottleVsyncReceived(777, mConnectionUid);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -518,27 +501,23 @@
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- // The first event will be seen by the interceptor, and not the connection.
+ // The first event will not be seen by the connection.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
- // The second event will be seen by the interceptor and the connection.
+ // The second event will be seen by the connection.
mCallback->onVSyncEvent(456, {123, 0});
- expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
- // The third event will be seen by the interceptor, and not the connection.
+ // The third event will not be seen by the connection.
mCallback->onVSyncEvent(789, {777, 744});
- expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
- // The fourth event will be seen by the interceptor and the connection.
+ // The fourth event will be seen by the connection.
mCallback->onVSyncEvent(101112, {7847, 86});
- expectInterceptCallReceived(101112);
expectVsyncEventReceivedByConnection(101112, 4u);
}
@@ -551,9 +530,8 @@
// Destroy the only (strong) reference to the connection.
mConnection = nullptr;
- // The first event will be seen by the interceptor, and not the connection.
+ // The first event will not be seen by the connection.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// EventThread should disable vsync callbacks
@@ -568,16 +546,12 @@
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- // The first event will be seen by the interceptor, and by the connection,
- // which then returns an error.
+ // The first event will be seen by the connection, which then returns an error.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
- // A subsequent event will be seen by the interceptor and not by the
- // connection.
+ // A subsequent event will not be seen by the connection.
mCallback->onVSyncEvent(456, {123, 0});
- expectInterceptCallReceived(456);
EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
// EventThread should disable vsync callbacks with the second event
@@ -599,10 +573,8 @@
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- // The first event will be seen by the interceptor, and by the connection,
- // which then returns an error.
+ // The first event will be seen by the connection, which then returns an error.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
1u);
@@ -617,16 +589,13 @@
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
- // The first event will be seen by the interceptor, and by the connection,
- // which then returns an non-fatal error.
+ // The first event will be seen by the connection, which then returns a non-fatal error.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
- // A subsequent event will be seen by the interceptor, and by the connection,
- // which still then returns an non-fatal error.
+ // A subsequent event will be seen by the connection, which still then returns a non-fatal
+ // error.
mCallback->onVSyncEvent(456, {123, 0});
- expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
// EventThread will not disable vsync callbacks as the errors are non-fatal.
@@ -664,9 +633,10 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
@@ -675,9 +645,10 @@
.setId(DisplayModeId(5))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
@@ -686,8 +657,9 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
+ const Fps fps = mode->getFps() / 2;
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -700,9 +672,10 @@
.setId(DisplayModeId(9))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
auto args = suppressConnectionEventRecorder.waitForCall();
ASSERT_FALSE(args.has_value());
@@ -748,17 +721,15 @@
expectVSyncSetEnabledCallReceived(true);
// Use the received callback to signal a first vsync event.
- // The interceptor should receive the event, but not the connection.
+ // The throttler should receive the event, but not the connection.
mCallback->onVSyncEvent(123, {456, 789});
- expectInterceptCallReceived(123);
expectThrottleVsyncReceived(456, mThrottledConnectionUid);
mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
// Use the received callback to signal a second vsync event.
- // The interceptor should receive the event, but the connection should
+ // The throttler should receive the event, but the connection should
// not as it was only interested in the first.
mCallback->onVSyncEvent(456, {123, 0});
- expectInterceptCallReceived(456);
expectThrottleVsyncReceived(123, mThrottledConnectionUid);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
new file mode 100644
index 0000000..6e4bf2b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -0,0 +1,96 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::hardware::graphics::composer::hal::HWDisplayId;
+using android::Hwc2::mock::PowerAdvisor;
+
+struct FakeDisplayInjectorArgs {
+ PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
+ HWDisplayId hwcDisplayId = 0;
+ bool isPrimary = true;
+};
+
+class FakeDisplayInjector {
+public:
+ FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+ sp<mock::NativeWindow> nativeWindow)
+ : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
+
+ sp<DisplayDevice> injectInternalDisplay(
+ const std::function<void(FakeDisplayDeviceInjector&)>& injectExtra,
+ FakeDisplayInjectorArgs args = {}) {
+ using testing::_;
+ using testing::AnyNumber;
+ using testing::DoAll;
+ using testing::Mock;
+ using testing::Return;
+ using testing::SetArgPointee;
+
+ constexpr ui::Size kResolution = {1080, 1920};
+
+ // The DisplayDevice is required to have a framebuffer (behind the
+ // ANativeWindow interface) which uses the actual hardware display
+ // size.
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getWidth()), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kResolution.getHeight()), Return(0)));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+ auto compositionDisplay = compositionengine::impl::
+ createDisplay(mFlinger.getCompositionEngine(),
+ compositionengine::DisplayCreationArgsBuilder()
+ .setId(args.displayId)
+ .setPixels(kResolution)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
+
+ auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal,
+ args.hwcDisplayId, args.isPrimary);
+
+ injector.setNativeWindow(mNativeWindow);
+ if (injectExtra) {
+ injectExtra(injector);
+ }
+
+ auto displayDevice = injector.inject();
+
+ Mock::VerifyAndClear(mNativeWindow.get());
+
+ return displayDevice;
+ }
+
+ TestableSurfaceFlinger& mFlinger;
+ Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+ sp<mock::NativeWindow> mNativeWindow;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
similarity index 100%
rename from services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
rename to services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 874fa7c..f47ac6d 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -480,7 +480,7 @@
addEmptyDisplayFrame();
auto displayFrame0 = getDisplayFrame(0);
- EXPECT_EQ(displayFrame0->getActuals().presentTime, -1);
+ EXPECT_EQ(displayFrame0->getActuals().presentTime, 59);
EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
@@ -2228,6 +2228,50 @@
EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
}
+TEST_F(FrameTimelineTest, jankClassification_presentFenceError) {
+ auto erroneousPresentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto erroneousPresentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto validPresentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken3 = mTokenManager->generateTokenForPredictions({72, 80, 80});
+
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(26, erroneousPresentFence1);
+
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(60, erroneousPresentFence2);
+
+ mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(80, validPresentFence);
+
+ validPresentFence->signalForTest(80);
+
+ addEmptyDisplayFrame();
+
+ {
+ auto displayFrame = getDisplayFrame(0);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 26);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+ }
+ {
+ auto displayFrame = getDisplayFrame(1);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 60);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown);
+ }
+ {
+ auto displayFrame = getDisplayFrame(2);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 80);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+ }
+}
+
TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
}
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 9d8e0a2..8f89a8c 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -73,6 +73,7 @@
EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
+ EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
}
};
@@ -145,6 +146,8 @@
{kMetadata2Name, kMetadata2Mandatory},
}),
Return(hardware::graphics::composer::V2_4::Error::NONE)));
+ EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE));
+
EXPECT_CALL(*mHal, registerCallback(_));
mHwc.setCallback(mCallback);
@@ -161,6 +164,7 @@
EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<aidl::Capability>{}));
EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
.WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+ EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED));
EXPECT_CALL(*mHal, registerCallback(_));
mHwc.setCallback(mCallback);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
new file mode 100644
index 0000000..8560902
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -0,0 +1,728 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+#define UPDATE_AND_VERIFY(HIERARCHY) \
+ ({ \
+ SCOPED_TRACE(""); \
+ updateAndVerify((HIERARCHY)); \
+ })
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = canBeRoot;
+ args.parentHandle = parent;
+ args.mirrorLayerHandle = mirror;
+ return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+ /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+ --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \
+ --gtest_shuffle \
+ --gtest_brief=1
+*/
+
+class LayerHierarchyTest : public testing::Test {
+protected:
+ LayerHierarchyTest() {
+ // tree with 3 levels of children
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(12, 1);
+ createLayer(13, 1);
+ createLayer(111, 11);
+ createLayer(121, 12);
+ createLayer(122, 12);
+ createLayer(1221, 122);
+ mLifecycleManager.commitChanges();
+ }
+ std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverseInZOrder(
+ [&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ void createRootLayer(uint32_t id) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
+ /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void reparentLayer(uint32_t id, uint32_t newParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (newParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
+ } else {
+ auto parentHandle = mHandles[newParentId];
+ transactions.back().states.front().state.parentSurfaceControlForChild =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(newParentId), "Test");
+ }
+ transactions.back().states.front().state.what = layer_state_t::eReparent;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (relativeParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ } else {
+ auto parentHandle = mHandles[relativeParentId];
+ transactions.back().states.front().state.relativeLayerSurfaceControl =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(relativeParentId), "test");
+ transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ }
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
+ auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
+ auto mirrorHandle =
+ (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
+
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
+ /*mirror=*/mHandles[layerToMirror])));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void updateBackgroundColor(uint32_t id, half alpha) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.bgColorAlpha = alpha;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+
+ void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
+ if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ hierarchyBuilder.update(mLifecycleManager.getLayers(),
+ mLifecycleManager.getDestroyedLayers());
+ }
+ mLifecycleManager.commitChanges();
+
+ // rebuild layer hierarchy from scratch and verify that it matches the updated state.
+ LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
+ getTraversalPath(newBuilder.getHierarchy()));
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
+ getTraversalPathInZOrder(newBuilder.getHierarchy()));
+ EXPECT_FALSE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ }
+
+ void setZ(uint32_t id, int32_t z) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().state.z = z;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+ LayerLifecycleManager mLifecycleManager;
+ std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+// reparenting tests
+TEST_F(LayerHierarchyTest, addLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ 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);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ createRootLayer(3);
+ createLayer(112, 11);
+ createLayer(12211, 1221);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, 11);
+ reparentLayer(111, 12);
+ reparentLayer(1221, 1);
+ reparentLayer(1221, 13);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, 13);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ destroyLayerHandle(2);
+ destroyLayerHandle(11);
+ destroyLayerHandle(1221);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ destroyLayerHandle(111);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // handle is destroyed but layer is kept alive and reachable by parent
+ 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);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // destroy parent layer and the child gets destroyed
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, 1);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ 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);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// offscreen tests
+TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ 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);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 112};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// rel-z tests
+TEST_F(LayerHierarchyTest, setRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ 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);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror tests
+TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ createLayer(1111, 111);
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 1111, 112, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 1111, 112, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror & relatives tests
+TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(111, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 111 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 11 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 111, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // 111 is not reachable in the mirror
+ expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 1221 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 12 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 1221,
+ 13, 14, 12, 121, 122, 1221, 1221, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // relative layer 1221 is traversable in the mirrored hierarchy as well
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentRelativeLayer(11, 2);
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(2);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // remove relative parent so layer becomes onscreen again
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // verify relz path
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // offscreen layer cannot be reached as a relative child
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {12, 121, 122, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // layer when onscreen can be reached as a relative child again
+ reparentLayer(12, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ updateBackgroundColor(1, 0.5);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// cycle tests
+TEST_F(LayerHierarchyTest, ParentBecomesTheChild) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createLayer(11, 1);
+ reparentLayer(1, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, RelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ 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));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, IndirectRelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(111, 11);
+ createLayer(21, 2);
+ createLayer(22, 2);
+ 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));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 2, 21};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(1, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ createRootLayer(1);
+ destroyLayerHandle(1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// traversal path test
+TEST_F(LayerHierarchyTest, traversalPathId) {
+ setZ(122, -1);
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ auto checkTraversalPathIdVisitor =
+ [](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id);
+ return true;
+ };
+ hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor);
+ hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 972198c..979924a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -69,11 +69,11 @@
// LayerHistory::summarize makes no guarantee of the order of the elements in the summary
// however, for testing only, a stable order is required, therefore we sort the list here.
// Any tests requiring ordered results must create layers with names.
- auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now);
+ auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
std::sort(summary.begin(), summary.end(),
- [](const RefreshRateConfigs::LayerRequirement& a,
- const RefreshRateConfigs::LayerRequirement& b) -> bool {
- return a.name < b.name;
+ [](const RefreshRateSelector::LayerRequirement& lhs,
+ const RefreshRateSelector::LayerRequirement& rhs) -> bool {
+ return lhs.name < rhs.name;
});
return summary;
}
@@ -125,16 +125,16 @@
ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
}
- std::shared_ptr<RefreshRateConfigs> mConfigs =
- std::make_shared<RefreshRateConfigs>(makeModes(createDisplayMode(DisplayModeId(0),
- LO_FPS),
- createDisplayMode(DisplayModeId(1),
- HI_FPS)),
- DisplayModeId(0));
+ std::shared_ptr<RefreshRateSelector> mSelector =
+ std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+ LO_FPS),
+ createDisplayMode(DisplayModeId(1),
+ HI_FPS)),
+ DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
+ TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
TestableSurfaceFlinger mFlinger;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
new file mode 100644
index 0000000..89440a6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -0,0 +1,453 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+using namespace android::surfaceflinger;
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = canBeRoot;
+ args.parentHandle = parent;
+ args.mirrorLayerHandle = mirror;
+ return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+ /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+ --gtest_filter="LayerLifecycleManagerTest.*" --gtest_repeat=100 \
+ --gtest_shuffle \
+ --gtest_brief=1
+*/
+class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener {
+public:
+ void onLayerAdded(const RequestedLayerState& layer) override {
+ mActualLayersAdded.emplace(layer.id);
+ };
+ void onLayerDestroyed(const RequestedLayerState& layer) override {
+ mActualLayersDestroyed.emplace(layer.id);
+ };
+
+ void expectLayersAdded(const std::unordered_set<uint32_t>& expectedLayersAdded) {
+ EXPECT_EQ(expectedLayersAdded, mActualLayersAdded);
+ mActualLayersAdded.clear();
+ }
+ void expectLayersDestroyed(const std::unordered_set<uint32_t>& expectedLayersDestroyed) {
+ EXPECT_EQ(expectedLayersDestroyed, mActualLayersDestroyed);
+ mActualLayersDestroyed.clear();
+ }
+
+ std::unordered_set<uint32_t> mActualLayersAdded;
+ std::unordered_set<uint32_t> mActualLayersDestroyed;
+};
+
+class LayerLifecycleManagerTest : public testing::Test {
+protected:
+ std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
+ return std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr));
+ }
+
+ std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
+ mHandles[parentId] = sp<LayerHandle>::make(parentId);
+ return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
+ /*parent=*/mHandles[parentId],
+ /*mirror=*/nullptr));
+ }
+
+ TransactionState reparentLayer(uint32_t id, uint32_t newParentId) {
+ TransactionState transaction;
+ transaction.states.push_back({});
+
+ if (newParentId == UNASSIGNED_LAYER_ID) {
+ transaction.states.front().state.parentSurfaceControlForChild = nullptr;
+ } else {
+ transaction.states.front().state.parentSurfaceControlForChild =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
+ sp<LayerHandle>::make(newParentId),
+ static_cast<int32_t>(newParentId), "Test");
+ }
+ transaction.states.front().state.what = layer_state_t::eReparent;
+ transaction.states.front().state.surface = sp<LayerHandle>::make(id);
+ return transaction;
+ }
+
+ TransactionState setLayer(uint32_t id, int32_t z) {
+ TransactionState transaction;
+ transaction.states.push_back({});
+ transaction.states.front().state.z = z;
+ transaction.states.front().state.what = layer_state_t::eLayerChanged;
+ transaction.states.front().state.surface = sp<LayerHandle>::make(id);
+ return transaction;
+ }
+
+ TransactionState makeRelative(uint32_t id, uint32_t relativeParentId) {
+ TransactionState transaction;
+ transaction.states.push_back({});
+
+ if (relativeParentId == UNASSIGNED_LAYER_ID) {
+ transaction.states.front().state.relativeLayerSurfaceControl = nullptr;
+ } else {
+ transaction.states.front().state.relativeLayerSurfaceControl =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
+ sp<LayerHandle>::make(relativeParentId),
+ static_cast<int32_t>(relativeParentId), "Test");
+ }
+ transaction.states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ transaction.states.front().state.surface = sp<LayerHandle>::make(id);
+ return transaction;
+ }
+
+ RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
+ uint32_t layerId) {
+ return lifecycleManager.getLayerFromId(layerId);
+ }
+
+ std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+TEST_F(LayerLifecycleManagerTest, addLayers) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(rootLayer(3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({1, 2, 3});
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ listener->expectLayersAdded({1, 2, 3});
+ listener->expectLayersDestroyed({1, 2, 3});
+}
+
+TEST_F(LayerLifecycleManagerTest, updateLayerStates) {
+ LayerLifecycleManager lifecycleManager;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.z = 2;
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ lifecycleManager.applyTransactions(transactions);
+ transactions.clear();
+
+ auto& managedLayers = lifecycleManager.getLayers();
+ ASSERT_EQ(managedLayers.size(), 1u);
+
+ EXPECT_EQ(managedLayers.front()->z, 2);
+ EXPECT_TRUE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ ASSERT_EQ(managedLayers.size(), 1u);
+ EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
+
+ // apply transactions that do not affect the hierarchy
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.backgroundBlurRadius = 22;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+ transactions.back().states.front().state.surface = handle;
+ lifecycleManager.applyTransactions(transactions);
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ EXPECT_EQ(managedLayers.front()->backgroundBlurRadius, 22u);
+}
+
+TEST_F(LayerLifecycleManagerTest, layerWithoutHandleIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.commitChanges();
+
+ SCOPED_TRACE("layerWithoutHandleIsDestroyed");
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({1});
+}
+
+TEST_F(LayerLifecycleManagerTest, rootLayerWithoutHandleIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({1});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenLayerIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.onHandlesDestroyed({3});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({3});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithHandleIsNotDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.onHandlesDestroyed({3});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({3});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithoutHandleIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.onHandlesDestroyed({3, 4});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({3, 4});
+}
+
+TEST_F(LayerLifecycleManagerTest, reparentingDoesNotAffectRelativeZ) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+ lifecycleManager.applyTransactions({reparentLayer(4, 2)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, reparentingToNullRemovesRelativeZ) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+ lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)});
+ EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, setZRemovesRelativeZ) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+ lifecycleManager.applyTransactions({setLayer(4, 1)});
+ EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ lifecycleManager.applyTransactions(transactions);
+
+ auto& managedLayers = lifecycleManager.getLayers();
+ ASSERT_EQ(managedLayers.size(), 2u);
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({});
+ EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf);
+}
+
+TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.surface = handle;
+
+ lifecycleManager.applyTransactions(transactions);
+
+ ASSERT_EQ(lifecycleManager.getLayers().size(), 1u);
+ ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 1u);
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({2});
+}
+
+TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ transactions.emplace_back();
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.onHandlesDestroyed({1});
+
+ ASSERT_EQ(lifecycleManager.getLayers().size(), 0u);
+ ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 2u);
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({1, 2});
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
deleted file mode 100644
index a706c4b..0000000
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ /dev/null
@@ -1,2376 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
-#include <ftl/enum.h>
-#include <ftl/fake_guard.h>
-#include <gmock/gmock.h>
-#include <log/log.h>
-#include <ui/Size.h>
-
-#include "DisplayHardware/HWC2.h"
-#include "FpsOps.h"
-#include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplayMode.h"
-
-using namespace std::chrono_literals;
-
-namespace android::scheduler {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-using LayerVoteType = RefreshRateConfigs::LayerVoteType;
-using LayerRequirement = RefreshRateConfigs::LayerRequirement;
-
-using mock::createDisplayMode;
-
-struct TestableRefreshRateConfigs : RefreshRateConfigs {
- using RefreshRateConfigs::RefreshRateConfigs;
- using RefreshRateConfigs::RefreshRateOrder;
-
- void setActiveModeId(DisplayModeId modeId) {
- ftl::FakeGuard guard(kMainThreadContext);
- return RefreshRateConfigs::setActiveModeId(modeId);
- }
-
- const DisplayMode& getActiveMode() const {
- ftl::FakeGuard guard(kMainThreadContext);
- return RefreshRateConfigs::getActiveMode();
- }
-
- DisplayModePtr getMinSupportedRefreshRate() const {
- std::lock_guard lock(mLock);
- return mMinRefreshRateModeIt->second;
- }
-
- DisplayModePtr getMaxSupportedRefreshRate() const {
- std::lock_guard lock(mLock);
- return mMaxRefreshRateModeIt->second;
- }
-
- DisplayModePtr getMinRefreshRateByPolicy() const {
- std::lock_guard lock(mLock);
- return getMinRefreshRateByPolicyLocked();
- }
-
- DisplayModePtr getMaxRefreshRateByPolicy() const {
- std::lock_guard lock(mLock);
- return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
- }
-
- std::vector<RefreshRateRanking> getRefreshRatesByPolicy(
- std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
- std::lock_guard lock(mLock);
- return RefreshRateConfigs::getRefreshRatesByPolicyLocked(anchorGroupOpt, refreshRateOrder);
- }
-
- const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
-
- using RefreshRateConfigs::GetRankedRefreshRatesCache;
- auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
-
- auto getRankedRefreshRatesAndSignals(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const {
- return RefreshRateConfigs::getRankedRefreshRates(layers, signals);
- }
-
- DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
- GlobalSignals signals = {}) const {
- return getRankedRefreshRatesAndSignals(layers, signals).first.front().displayModePtr;
- }
-};
-
-class RefreshRateConfigsTest : public testing::Test {
-protected:
- RefreshRateConfigsTest();
- ~RefreshRateConfigsTest();
-
- static constexpr DisplayModeId kModeId60{0};
- static constexpr DisplayModeId kModeId90{1};
- static constexpr DisplayModeId kModeId72{2};
- static constexpr DisplayModeId kModeId120{3};
- static constexpr DisplayModeId kModeId30{4};
- static constexpr DisplayModeId kModeId25{5};
- static constexpr DisplayModeId kModeId50{6};
- static constexpr DisplayModeId kModeId24{7};
- static constexpr DisplayModeId kModeId24Frac{8};
- static constexpr DisplayModeId kModeId30Frac{9};
- static constexpr DisplayModeId kModeId60Frac{10};
-
- static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
- static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
- static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
- static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1);
- static inline const DisplayModePtr kMode90_4K =
- createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160});
- static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz);
- static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1);
- static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
- static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1);
- static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz);
- static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1);
- static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
- static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
- static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
- static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
- static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
- static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
-
- // Test configurations.
- static inline const DisplayModes kModes_60 = makeModes(kMode60);
- static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
- static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
- static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
- static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72);
- static inline const DisplayModes kModes_60_90_72_120 =
- makeModes(kMode60, kMode90, kMode72, kMode120);
- static inline const DisplayModes kModes_30_60_72_90_120 =
- makeModes(kMode60, kMode90, kMode72, kMode120, kMode30);
-
- static inline const DisplayModes kModes_30_60 =
- makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30);
- static inline const DisplayModes kModes_30_60_72_90 =
- makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30);
- static inline const DisplayModes kModes_30_60_90 =
- makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30);
- static inline const DisplayModes kModes_25_30_50_60 =
- makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
- static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
-
- // This is a typical TV configuration.
- static inline const DisplayModes kModes_24_25_30_50_60_Frac =
- makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
- kMode60Frac);
-};
-
-RefreshRateConfigsTest::RefreshRateConfigsTest() {
- 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());
-}
-
-RefreshRateConfigsTest::~RefreshRateConfigsTest() {
- 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());
-}
-
-namespace {
-
-TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) {
- RefreshRateConfigs configs(kModes_60, kModeId60);
- EXPECT_FALSE(configs.canSwitch());
-}
-
-TEST_F(RefreshRateConfigsTest, invalidPolicy) {
- RefreshRateConfigs configs(kModes_60, kModeId60);
- EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
- EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- const auto minRate = configs.getMinSupportedRefreshRate();
- const auto performanceRate = configs.getMaxSupportedRefreshRate();
-
- EXPECT_EQ(kMode60, minRate);
- EXPECT_EQ(kMode90, performanceRate);
-
- const auto minRateByPolicy = configs.getMinRefreshRateByPolicy();
- const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(minRateByPolicy, minRate);
- EXPECT_EQ(performanceRateByPolicy, performanceRate);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- const auto minRate = configs.getMinRefreshRateByPolicy();
- const auto performanceRate = configs.getMaxSupportedRefreshRate();
- const auto minRate60 = configs.getMinRefreshRateByPolicy();
- const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(kMode60, minRate);
- EXPECT_EQ(kMode60, minRate60);
- EXPECT_EQ(kMode60, performanceRate60);
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
- configs.setActiveModeId(kModeId90);
-
- const auto minRate90 = configs.getMinRefreshRateByPolicy();
- const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(kMode90_G1, performanceRate);
- EXPECT_EQ(kMode90_G1, minRate90);
- EXPECT_EQ(kMode90_G1, performanceRate90);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
- TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60);
-
- const auto minRate = configs.getMinRefreshRateByPolicy();
- const auto performanceRate = configs.getMaxSupportedRefreshRate();
- const auto minRate60 = configs.getMinRefreshRateByPolicy();
- const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(kMode60, minRate);
- EXPECT_EQ(kMode60, minRate60);
- EXPECT_EQ(kMode60, performanceRate60);
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
- configs.setActiveModeId(kModeId90);
-
- const auto minRate90 = configs.getMinRefreshRateByPolicy();
- const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(kMode90_4K, performanceRate);
- EXPECT_EQ(kMode90_4K, minRate90);
- EXPECT_EQ(kMode90_4K, performanceRate90);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_policyChange) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- const auto minRate = configs.getMinRefreshRateByPolicy();
- const auto performanceRate = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(kMode60, minRate);
- EXPECT_EQ(kMode90, performanceRate);
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
-
- const auto minRate60 = configs.getMinRefreshRateByPolicy();
- const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
-
- EXPECT_EQ(kMode60, minRate60);
- EXPECT_EQ(kMode60, performanceRate60);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
- {
- const auto& mode = configs.getActiveMode();
- EXPECT_EQ(mode.getId(), kModeId60);
- }
-
- configs.setActiveModeId(kModeId90);
- {
- const auto& mode = configs.getActiveMode();
- EXPECT_EQ(mode.getId(), kModeId90);
- }
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
- {
- const auto& mode = configs.getActiveMode();
- EXPECT_EQ(mode.getId(), kModeId90);
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
- {
- TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72);
-
- // If there are no layers we select the default frame rate, which is the max of the primary
- // range.
- EXPECT_EQ(kMode90, configs.getBestRefreshRate());
-
- EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR);
- EXPECT_EQ(kMode60, configs.getBestRefreshRate());
- }
- {
- // We select max even when this will cause a non-seamless switch.
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
- constexpr bool kAllowGroupSwitching = true;
- EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}),
- NO_ERROR);
- EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate());
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::Min;
- lr.name = "Min";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- lr.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.name = "";
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
-
- lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
-
- lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0);
- lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::Min;
- lr.name = "Min";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- lr.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
- TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 48_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 48_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::Heuristic;
- lr1.name = "24Hz Heuristic";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
- {.frameRateMultipleThreshold = 120});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
- auto& lr3 = layers[2];
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::Heuristic;
- lr1.name = "24Hz Heuristic";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Max;
- lr2.name = "Max";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 120_Hz;
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.name = "120Hz ExplicitDefault";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 24_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 120_Hz;
- lr2.vote = LayerVoteType::ExplicitExact;
- lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 10_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "30Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 120_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- lr1.desiredRefreshRate = 30_Hz;
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.name = "30Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = 30_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.name = "30Hz ExplicitExactOrMultiple";
- lr3.vote = LayerVoteType::Heuristic;
- lr3.desiredRefreshRate = 120_Hz;
- lr3.name = "120Hz Heuristic";
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
- TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::Min;
- lr.name = "Min";
- EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- lr.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 90_Hz;
- lr.vote = LayerVoteType::Heuristic;
- lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
- lr.desiredRefreshRate = 45_Hz;
- lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
- lr.desiredRefreshRate = 30_Hz;
- lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
- lr.desiredRefreshRate = 24_Hz;
- lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
- lr.desiredRefreshRate = 24_Hz;
- lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr.name = "24Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
- TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::Min;
- lr2.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Min;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Min;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Max;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Max;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 15_Hz;
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 30_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
- lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = configs.getBestRefreshRate(layers);
- EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
- << to_string(mode->getFps());
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
- TestableRefreshRateConfigs configs(kModes_60_120, kModeId60,
- {.frameRateMultipleThreshold = 120});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
- lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = configs.getBestRefreshRate(layers);
- EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
- << to_string(mode->getFps());
- }
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 60_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 90_Hz;
- lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
- lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = configs.getBestRefreshRate(layers, {});
- EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
- << to_string(mode->getFps());
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 90_Hz;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.desiredRefreshRate = 90_Hz;
- lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Max;
- lr2.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 30_Hz;
- lr1.name = "30Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 90_Hz;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 30_Hz;
- lr1.name = "30Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Max;
- lr2.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::NoVote;
- lr2.name = "NoVote";
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::NoVote;
- lr2.name = "NoVote";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Max;
- lr2.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Max;
- lr2.name = "Max";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- // The other layer starts to provide buffers
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 90_Hz;
- lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicy) {
- // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
- // different group.
- TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode30}};
-
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
- TestableRefreshRateConfigs::RefreshRateOrder::
- Descending);
-
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicy) {
- // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
- // different group.
- TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode90}};
-
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(configs.getActiveMode().getGroup(),
- TestableRefreshRateConfigs::RefreshRateOrder::
- Ascending);
-
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
- // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
- // different group.
- TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode30},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode90}};
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}), 0);
-
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
- TestableRefreshRateConfigs::RefreshRateOrder::
- Ascending);
-
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
- // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
- // different group.
- TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId72);
- const std::vector<RefreshRateRanking>& expectedRefreshRates = {RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode60},
- RefreshRateRanking{kMode30}};
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}), 0);
-
- const std::vector<RefreshRateRanking>& refreshRates =
- configs.getRefreshRatesByPolicy(/*anchorGroupOpt*/ std::nullopt,
- TestableRefreshRateConfigs::RefreshRateOrder::
- Descending);
-
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-}
-
-TEST_F(RefreshRateConfigsTest, powerOnImminentConsidered) {
- RefreshRateConfigs configs(kModes_60_90, kModeId60);
- std::vector<RefreshRateRanking> expectedRefreshRates = {RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode60}};
-
- auto [refreshRates, signals] = configs.getRankedRefreshRates({}, {});
- EXPECT_FALSE(signals.powerOnImminent);
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-
- std::tie(refreshRates, signals) = configs.getRankedRefreshRates({}, {.powerOnImminent = true});
- EXPECT_TRUE(signals.powerOnImminent);
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr1 = layers[0];
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
-
- std::tie(refreshRates, signals) =
- configs.getRankedRefreshRates(layers, {.powerOnImminent = true});
- EXPECT_TRUE(signals.powerOnImminent);
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-
- expectedRefreshRates = {RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90}};
- std::tie(refreshRates, signals) =
- configs.getRankedRefreshRates(layers, {.powerOnImminent = false});
- EXPECT_FALSE(signals.powerOnImminent);
- ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
- for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i].displayModePtr, refreshRates[i].displayModePtr)
- << "Expected fps " << expectedRefreshRates[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].displayModePtr->getFps().getIntValue();
- }
-}
-
-TEST_F(RefreshRateConfigsTest, touchConsidered) {
- RefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- auto [_, signals] = configs.getRankedRefreshRates({}, {});
- EXPECT_FALSE(signals.touch);
-
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates({}, {.touch = true});
- EXPECT_TRUE(signals.touch);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
- EXPECT_TRUE(signals.touch);
-
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitDefault";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
- EXPECT_FALSE(signals.touch);
-
- lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitExactOrMultiple";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
- EXPECT_TRUE(signals.touch);
-
- lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = 60_Hz;
- lr1.name = "60Hz ExplicitDefault";
- lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz Heuristic";
- std::tie(std::ignore, signals) = configs.getRankedRefreshRates(layers, {.touch = true});
- EXPECT_FALSE(signals.touch);
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
- TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- // Prepare a table with the vote and the expected refresh rate
- const std::initializer_list<std::pair<Fps, Fps>> testCases = {
- {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz},
-
- {100_Hz, 90_Hz}, {90_Hz, 90_Hz}, {89_Hz, 90_Hz},
-
- {80_Hz, 72_Hz}, {73_Hz, 72_Hz}, {72_Hz, 72_Hz}, {71_Hz, 72_Hz}, {70_Hz, 72_Hz},
-
- {65_Hz, 60_Hz}, {60_Hz, 60_Hz}, {59_Hz, 60_Hz}, {58_Hz, 60_Hz},
-
- {55_Hz, 90_Hz}, {50_Hz, 90_Hz}, {45_Hz, 90_Hz},
-
- {42_Hz, 120_Hz}, {40_Hz, 120_Hz}, {39_Hz, 120_Hz},
-
- {37_Hz, 72_Hz}, {36_Hz, 72_Hz}, {35_Hz, 72_Hz},
-
- {30_Hz, 60_Hz},
- };
-
- for (auto [desired, expected] : testCases) {
- lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = desired;
-
- std::stringstream ss;
- ss << "ExplicitDefault " << desired;
- lr.name = ss.str();
-
- EXPECT_EQ(expected, configs.getBestRefreshRate(layers)->getFps());
- }
-}
-
-TEST_F(RefreshRateConfigsTest,
- getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- // Test that 23.976 will choose 24 if 23.976 is not supported
- {
- TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
-
- lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr.desiredRefreshRate = 23.976_Hz;
- lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
- }
-
- // Test that 24 will choose 23.976 if 24 is not supported
- {
- TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
-
- lr.desiredRefreshRate = 24_Hz;
- lr.name = "ExplicitExactOrMultiple 24 Hz";
- EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
- }
-
- // Test that 29.97 will prefer 59.94 over 60 and 30
- {
- TestableRefreshRateConfigs configs(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
- kMode60, kMode60Frac),
- kModeId60);
-
- lr.desiredRefreshRate = 29.97_Hz;
- lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers)->getId());
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- // Test that voting for supported refresh rate will select this refresh rate
- {
- TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60);
-
- for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
- lr.vote = LayerVoteType::ExplicitExact;
- lr.desiredRefreshRate = desired;
- std::stringstream ss;
- ss << "ExplicitExact " << desired;
- lr.name = ss.str();
-
- EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps());
- }
- }
-
- // Test that 23.976 will choose 24 if 23.976 is not supported
- {
- TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
-
- lr.vote = LayerVoteType::ExplicitExact;
- lr.desiredRefreshRate = 23.976_Hz;
- lr.name = "ExplicitExact 23.976 Hz";
- EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
- }
-
- // Test that 24 will choose 23.976 if 24 is not supported
- {
- TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
-
- lr.desiredRefreshRate = 24_Hz;
- lr.name = "ExplicitExact 24 Hz";
- EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
- }
-}
-
-TEST_F(RefreshRateConfigsTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
- RefreshRateConfigs configs(kModes_60_90, kModeId90);
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz ExplicitDefault";
- lr.focused = true;
-
- const auto [mode, signals] =
- configs.getRankedRefreshRates(layers, {.touch = true, .idle = true});
-
- EXPECT_EQ(mode.begin()->displayModePtr, kMode60);
- EXPECT_FALSE(signals.touch);
-}
-
-TEST_F(RefreshRateConfigsTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = 90_Hz;
- lr.name = "90Hz ExplicitDefault";
- lr.focused = true;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true}));
-}
-
-TEST_F(RefreshRateConfigsTest, testDisplayModeOrdering) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f},
- {.weight = 1.f},
- {.weight = 1.f},
- {.weight = 1.f},
- {.weight = 1.f}};
- auto& lr1 = layers[0];
- auto& lr2 = layers[1];
- auto& lr3 = layers[2];
- auto& lr4 = layers[3];
- auto& lr5 = layers[4];
-
- lr1.desiredRefreshRate = 90_Hz;
- lr1.name = "90Hz";
- lr1.focused = true;
-
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz";
- lr2.focused = true;
-
- lr3.desiredRefreshRate = 72_Hz;
- lr3.name = "72Hz";
- lr3.focused = true;
-
- lr4.desiredRefreshRate = 120_Hz;
- lr4.name = "120Hz";
- lr4.focused = true;
-
- lr5.desiredRefreshRate = 30_Hz;
- lr5.name = "30Hz";
- lr5.focused = true;
-
- std::vector<RefreshRateRanking> expectedRankings = {
- RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
- RefreshRateRanking{kMode60}, RefreshRateRanking{kMode30},
- };
-
- std::vector<RefreshRateRanking> actualOrder =
- configs.getRankedRefreshRatesAndSignals(layers, {}).first;
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
- }
-
- lr1.vote = LayerVoteType::Max;
- lr1.name = "Max";
-
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz";
-
- lr3.desiredRefreshRate = 72_Hz;
- lr3.name = "72Hz";
-
- lr4.desiredRefreshRate = 90_Hz;
- lr4.name = "90Hz";
-
- lr5.desiredRefreshRate = 120_Hz;
- lr5.name = "120Hz";
-
- expectedRankings = {
- RefreshRateRanking{kMode120}, RefreshRateRanking{kMode90}, RefreshRateRanking{kMode72},
- RefreshRateRanking{kMode60}, RefreshRateRanking{kMode30},
- };
-
- actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
-
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
- }
-
- lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = 30_Hz;
- lr1.name = "30Hz";
-
- lr2.desiredRefreshRate = 120_Hz;
- lr2.name = "120Hz";
-
- lr3.desiredRefreshRate = 60_Hz;
- lr3.name = "60Hz";
-
- lr5.desiredRefreshRate = 72_Hz;
- lr5.name = "72Hz";
-
- expectedRankings = {
- RefreshRateRanking{kMode30}, RefreshRateRanking{kMode60}, RefreshRateRanking{kMode90},
- RefreshRateRanking{kMode120}, RefreshRateRanking{kMode72},
- };
-
- actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
- }
-
- lr1.desiredRefreshRate = 120_Hz;
- lr1.name = "120Hz";
- lr1.weight = 0.0f;
-
- lr2.desiredRefreshRate = 60_Hz;
- lr2.name = "60Hz";
- lr2.vote = LayerVoteType::NoVote;
-
- lr3.name = "60Hz-2";
- lr3.vote = LayerVoteType::Heuristic;
-
- lr4.vote = LayerVoteType::ExplicitExact;
-
- lr5.desiredRefreshRate = 120_Hz;
- lr5.name = "120Hz-2";
-
- expectedRankings = {
- RefreshRateRanking{kMode90}, RefreshRateRanking{kMode60}, RefreshRateRanking{kMode120},
- RefreshRateRanking{kMode72}, RefreshRateRanking{kMode30},
- };
-
- actualOrder = configs.getRankedRefreshRatesAndSignals(layers, {}).first;
- ASSERT_EQ(expectedRankings.size(), actualOrder.size());
- for (size_t i = 0; i < expectedRankings.size(); ++i) {
- EXPECT_EQ(expectedRankings[i].displayModePtr, actualOrder[i].displayModePtr)
- << "Expected fps " << expectedRankings[i].displayModePtr->getFps().getIntValue()
- << " Actual fps " << actualOrder[i].displayModePtr->getFps().getIntValue();
- }
-}
-
-TEST_F(RefreshRateConfigsTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
-
- const auto [mode, signals] = configs.getRankedRefreshRatesAndSignals({}, {});
- EXPECT_EQ(mode.front().displayModePtr, kMode90);
- EXPECT_FALSE(signals.touch);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& lr = layers[0];
-
- lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz ExplicitExactOrMultiple";
- lr.focused = false;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.focused = true;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz ExplicitDefault";
- lr.focused = false;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.focused = true;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Heuristic;
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz Heuristic";
- lr.focused = false;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.focused = true;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Max;
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz Max";
- lr.focused = false;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.focused = true;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.vote = LayerVoteType::Min;
- lr.desiredRefreshRate = 60_Hz;
- lr.name = "60Hz Min";
- lr.focused = false;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- lr.focused = true;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- // The default policy doesn't allow group switching. Verify that no
- // group switches are performed.
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = 90_Hz;
- layer.seamlessness = Seamlessness::SeamedAndSeamless;
- layer.name = "90Hz ExplicitDefault";
- layer.focused = true;
-
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = 90_Hz;
- layer.seamlessness = Seamlessness::SeamedAndSeamless;
- layer.name = "90Hz ExplicitDefault";
- layer.focused = true;
- EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- // Verify that we won't change the group if seamless switch is required.
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = 90_Hz;
- layer.seamlessness = Seamlessness::OnlySeamless;
- layer.name = "90Hz ExplicitDefault";
- layer.focused = true;
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- configs.setActiveModeId(kModeId90);
-
- // Verify that we won't do a seamless switch if we request the same mode as the default
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = 60_Hz;
- layer.seamlessness = Seamlessness::OnlySeamless;
- layer.name = "60Hz ExplicitDefault";
- layer.focused = true;
- EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- configs.setActiveModeId(kModeId90);
-
- // Verify that if the current config is in another group and there are no layers with
- // seamlessness=SeamedAndSeamless we'll go back to the default group.
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = 60_Hz;
- layer.seamlessness = Seamlessness::Default;
- layer.name = "60Hz ExplicitDefault";
- layer.focused = true;
-
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- configs.setActiveModeId(kModeId90);
-
- // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
- // seamlessness=OnlySeamless can't change the mode group.
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].vote = LayerVoteType::ExplicitDefault;
- layers[0].desiredRefreshRate = 60_Hz;
- layers[0].seamlessness = Seamlessness::OnlySeamless;
- layers[0].name = "60Hz ExplicitDefault";
- layers[0].focused = true;
-
- layers.push_back(LayerRequirement{.weight = 0.5f});
- layers[1].vote = LayerVoteType::ExplicitDefault;
- layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
- layers[1].desiredRefreshRate = 90_Hz;
- layers[1].name = "90Hz ExplicitDefault";
- layers[1].focused = false;
-
- EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- configs.setActiveModeId(kModeId90);
-
- // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
- // seamlessness=Default can't change the mode group back to the group of the default
- // mode.
- // For example, this may happen when a video playback requests and gets a seamed switch,
- // but another layer (with default seamlessness) starts animating. The animating layer
- // should not cause a seamed switch.
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].seamlessness = Seamlessness::Default;
- layers[0].desiredRefreshRate = 60_Hz;
- layers[0].focused = true;
- layers[0].vote = LayerVoteType::ExplicitDefault;
- layers[0].name = "60Hz ExplicitDefault";
-
- layers.push_back(LayerRequirement{.weight = 0.1f});
- layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
- layers[1].desiredRefreshRate = 90_Hz;
- layers[1].focused = true;
- layers[1].vote = LayerVoteType::ExplicitDefault;
- layers[1].name = "90Hz ExplicitDefault";
-
- EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- configs.setActiveModeId(kModeId90);
-
- // Layer with seamlessness=Default can change the mode group if there's a not
- // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
- // when in split screen mode the user switches between the two visible applications.
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].seamlessness = Seamlessness::Default;
- layers[0].desiredRefreshRate = 60_Hz;
- layers[0].focused = true;
- layers[0].vote = LayerVoteType::ExplicitDefault;
- layers[0].name = "60Hz ExplicitDefault";
-
- layers.push_back(LayerRequirement{.weight = 0.7f});
- layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
- layers[1].desiredRefreshRate = 90_Hz;
- layers[1].focused = false;
- layers[1].vote = LayerVoteType::ExplicitDefault;
- layers[1].name = "90Hz ExplicitDefault";
-
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
- TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
-
- // Allow group switching.
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::ExplicitExactOrMultiple;
- layer.desiredRefreshRate = 60_Hz;
- layer.seamlessness = Seamlessness::SeamedAndSeamless;
- layer.name = "60Hz ExplicitExactOrMultiple";
- layer.focused = true;
-
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-
- configs.setActiveModeId(kModeId120);
- EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
- TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60);
-
- // Allow group switching.
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
- .vote = LayerVoteType::ExplicitDefault,
- .desiredRefreshRate = 60_Hz,
- .seamlessness = Seamlessness::SeamedAndSeamless,
- .weight = 0.5f,
- .focused = false},
- {.name = "25Hz ExplicitExactOrMultiple",
- .vote = LayerVoteType::ExplicitExactOrMultiple,
- .desiredRefreshRate = 25_Hz,
- .seamlessness = Seamlessness::OnlySeamless,
- .weight = 1.f,
- .focused = true}};
-
- EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers)->getId());
-
- auto& seamedLayer = layers[0];
- seamedLayer.desiredRefreshRate = 30_Hz;
- seamedLayer.name = "30Hz ExplicitDefault";
- configs.setActiveModeId(kModeId30);
-
- EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
- TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90);
-
- // Allow group switching.
- RefreshRateConfigs::Policy policy;
- policy.defaultMode = configs.getCurrentPolicy().defaultMode;
- policy.allowGroupSwitching = true;
- EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
- std::vector<LayerRequirement> layers = {
- {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
-
- EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
- TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].name = "Test layer";
-
- struct Args {
- bool touch = false;
- bool focused = true;
- };
-
- // Return the config ID from calling getBestRefreshRate() for a single layer with the
- // given voteType and fps.
- auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId {
- layers[0].vote = voteType;
- layers[0].desiredRefreshRate = fps;
- layers[0].focused = args.focused;
- return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId();
- };
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0);
-
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId());
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
- EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
- EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
-
- // Unfocused layers are not allowed to override primary config.
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
- EXPECT_EQ(kModeId60,
- getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
-
- // Touch boost should be restricted to the primary range.
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
-
- // When we're higher than the primary range max due to a layer frame rate setting, touch boost
- // shouldn't drag us back down to the primary range max.
- EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
- EXPECT_EQ(kModeId60,
- getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0);
-
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
- EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
-}
-
-TEST_F(RefreshRateConfigsTest, idle) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].name = "Test layer";
-
- const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
- layers[0].vote = voteType;
- layers[0].desiredRefreshRate = 90_Hz;
-
- const auto [refreshRate, signals] =
- configs.getRankedRefreshRatesAndSignals(layers,
- {.touch = touchActive, .idle = true});
-
- // Refresh rate will be chosen by either touch state or idle state.
- EXPECT_EQ(!touchActive, signals.idle);
- return refreshRate.front().displayModePtr->getId();
- };
-
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
-
- // Idle should be lower priority than touch boost.
- {
- constexpr bool kTouchActive = true;
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
- EXPECT_EQ(kModeId90,
- getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
- }
-
- // With no layers, idle should still be lower priority than touch boost.
- EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
-
- // Idle should be higher precedence than other layer frame rate considerations.
- configs.setActiveModeId(kModeId90);
-
- {
- constexpr bool kTouchActive = false;
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
- EXPECT_EQ(kModeId60,
- getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
- }
-
- // Idle should be applied rather than the current config when there are no layers.
- EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true})->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
- const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps));
- const Fps expectedFrameRate = [fps] {
- if (fps < 26.91f) return 24_Hz;
- if (fps < 37.51f) return 30_Hz;
- if (fps < 52.51f) return 45_Hz;
- if (fps < 66.01f) return 60_Hz;
- if (fps < 81.01f) return 72_Hz;
- return 90_Hz;
- }();
-
- EXPECT_EQ(expectedFrameRate, knownFrameRate);
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
- TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
- struct Expectation {
- Fps fps;
- DisplayModePtr mode;
- };
-
- const std::initializer_list<Expectation> knownFrameRatesExpectations = {
- {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90},
- {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90},
- };
-
- // Make sure the test tests all the known frame rate
- const auto& knownFrameRates = configs.knownFrameRates();
- const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(),
- knownFrameRatesExpectations.begin(),
- [](Fps fps, const Expectation& expected) {
- return isApproxEqual(fps, expected.fps);
- });
- EXPECT_TRUE(equal);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- auto& layer = layers[0];
- layer.vote = LayerVoteType::Heuristic;
-
- for (const auto& [fps, mode] : knownFrameRatesExpectations) {
- layer.desiredRefreshRate = fps;
- EXPECT_EQ(mode, configs.getBestRefreshRate(layers));
- }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- auto& explicitExactLayer = layers[0];
- auto& explicitExactOrMultipleLayer = layers[1];
-
- explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
- explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
- explicitExactLayer.vote = LayerVoteType::ExplicitExact;
- explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = 30_Hz;
-
- EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers, {.touch = true}));
-
- explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
- explicitExactLayer.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 72_Hz;
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 120_Hz;
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
- {.enableFrameRateOverride = true});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- auto& explicitExactLayer = layers[0];
- auto& explicitExactOrMultipleLayer = layers[1];
-
- explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
- explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
- explicitExactLayer.vote = LayerVoteType::ExplicitExact;
- explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = 30_Hz;
-
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
-
- explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
- explicitExactLayer.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 72_Hz;
- EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 120_Hz;
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
- using GlobalSignals = RefreshRateConfigs::GlobalSignals;
- const auto args = std::make_pair(std::vector<LayerRequirement>{},
- GlobalSignals{.touch = true, .idle = true});
-
- const auto result = std::make_pair(std::vector<RefreshRateRanking>{RefreshRateRanking{kMode90}},
- GlobalSignals{.touch = true});
-
- configs.mutableGetRankedRefreshRatesCache() = {args, result};
-
- EXPECT_EQ(result, configs.getRankedRefreshRatesAndSignals(args.first, args.second));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
- EXPECT_FALSE(configs.mutableGetRankedRefreshRatesCache());
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
-
- const auto result = configs.getRankedRefreshRatesAndSignals(layers, globalSignals);
-
- const auto& cache = configs.mutableGetRankedRefreshRatesCache();
- ASSERT_TRUE(cache);
-
- EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
- EXPECT_EQ(cache->result, result);
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
- TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- auto& explicitExactLayer = layers[0];
- auto& explicitExactOrMultipleLayer = layers[1];
-
- explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
- explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
- explicitExactLayer.vote = LayerVoteType::ExplicitExact;
- explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = 30_Hz;
-
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
-
- explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
-
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers, {.touch = true}));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
- TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60,
- {.enableFrameRateOverride = true});
-
- std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
- auto& explicitDefaultLayer = layers[0];
- auto& explicitExactOrMultipleLayer = layers[1];
-
- explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
- explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
- explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
- explicitDefaultLayer.name = "ExplicitDefault";
- explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
-
- EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-// b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) {
- constexpr int kMinRefreshRate = 10;
- constexpr int kMaxRefreshRate = 240;
-
- DisplayModes displayModes;
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const DisplayModeId modeId(fps);
- displayModes.try_emplace(modeId,
- createDisplayMode(modeId,
- Fps::fromValue(static_cast<float>(fps))));
- }
-
- const TestableRefreshRateConfigs configs(std::move(displayModes),
- DisplayModeId(kMinRefreshRate));
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
- layers[0].desiredRefreshRate = fps;
- layers[0].vote = vote;
- EXPECT_EQ(fps.getIntValue(), configs.getBestRefreshRate(layers)->getFps().getIntValue())
- << "Failed for " << ftl::enum_string(vote);
- };
-
- for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
- const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
- testRefreshRate(refreshRate, LayerVoteType::Heuristic);
- testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
- testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
- testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
- }
-}
-
-// b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
- constexpr DisplayModeId kActiveModeId{0};
- DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
- createDisplayMode(DisplayModeId(1), 53_Hz),
- createDisplayMode(DisplayModeId(2), 55_Hz),
- createDisplayMode(DisplayModeId(3), 60_Hz));
-
- const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
- const TestableRefreshRateConfigs configs(std::move(displayModes), kActiveModeId);
-
- const std::vector<LayerRequirement> layers = {
- {
- .vote = LayerVoteType::ExplicitDefault,
- .desiredRefreshRate = 43_Hz,
- .seamlessness = Seamlessness::SeamedAndSeamless,
- .weight = 0.41f,
- },
- {
- .vote = LayerVoteType::ExplicitExactOrMultiple,
- .desiredRefreshRate = 53_Hz,
- .seamlessness = Seamlessness::SeamedAndSeamless,
- .weight = 0.41f,
- },
- };
-
- EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals)->getFps());
-}
-
-TEST_F(RefreshRateConfigsTest, modeComparison) {
- EXPECT_LT(kMode60->getFps(), kMode90->getFps());
- EXPECT_GE(kMode60->getFps(), kMode60->getFps());
- EXPECT_GE(kMode90->getFps(), kMode90->getFps());
-}
-
-TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
- using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
-
- RefreshRateConfigs configs(kModes_60_90, kModeId90);
-
- // SetPolicy(60, 90), current 90Hz => TurnOn.
- EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
- // SetPolicy(60, 90), current 60Hz => TurnOn.
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
- // SetPolicy(60, 60), current 60Hz => TurnOff
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-
- // SetPolicy(90, 90), current 90Hz => TurnOff.
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-}
-
-TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
- using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
-
- RefreshRateConfigs configs(kModes_60_120, kModeId120);
-
- // SetPolicy(0, 60), current 60Hz => TurnOn.
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
- // SetPolicy(60, 60), current 60Hz => TurnOff.
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-
- // SetPolicy(60, 120), current 60Hz => TurnOn.
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
- // SetPolicy(120, 120), current 120Hz => TurnOff.
- EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0);
- EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) {
- TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
-
- const auto frameRate = 30_Hz;
- Fps displayRefreshRate = configs.getActiveMode().getFps();
- EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
- configs.setActiveModeId(kModeId60);
- displayRefreshRate = configs.getActiveMode().getFps();
- EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
- configs.setActiveModeId(kModeId72);
- displayRefreshRate = configs.getActiveMode().getFps();
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
- configs.setActiveModeId(kModeId90);
- displayRefreshRate = configs.getActiveMode().getFps();
- EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
- configs.setActiveModeId(kModeId120);
- displayRefreshRate = configs.getActiveMode().getFps();
- EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
- configs.setActiveModeId(kModeId90);
- displayRefreshRate = configs.getActiveMode().getFps();
- EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
-
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 23.976_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(30_Hz, 29.97_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(60_Hz, 59.94_Hz));
-}
-
-TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
-
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
-
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
-
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
-
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
- EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
-
- const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
- for (auto refreshRate : refreshRates) {
- EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate));
- }
-
- EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz));
- EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
- EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
- RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120);
-
- EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
- RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = true});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].name = "Test layer";
- layers[0].ownerUid = 1234;
- layers[0].desiredRefreshRate = 60_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
-
- auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::NoVote;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
- RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = true});
-
- std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
- {.ownerUid = 5678, .weight = 1.f}};
-
- layers[0].name = "Test layer 1234";
- layers[0].desiredRefreshRate = 60_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
-
- layers[1].name = "Test layer 5678";
- layers[1].desiredRefreshRate = 30_Hz;
- layers[1].vote = LayerVoteType::ExplicitDefault;
- auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-
- EXPECT_EQ(2u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
- ASSERT_EQ(1u, frameRateOverrides.count(5678));
- EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
-
- layers[1].vote = LayerVoteType::Heuristic;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[1].ownerUid = 1234;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
- RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = true});
-
- std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
- layers[0].name = "Test layer";
- layers[0].desiredRefreshRate = 60_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
-
- auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::ExplicitExact;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
- EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-} // namespace
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
new file mode 100644
index 0000000..a3b3c4c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -0,0 +1,2960 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <algorithm>
+#include <array>
+
+#include <ftl/enum.h>
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <ui/Size.h>
+
+#include <scheduler/FrameRateMode.h>
+#include "DisplayHardware/HWC2.h"
+#include "FpsOps.h"
+#include "Scheduler/RefreshRateSelector.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include "libsurfaceflinger_unittest_main.h"
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+using Config = RefreshRateSelector::Config;
+using LayerRequirement = RefreshRateSelector::LayerRequirement;
+using LayerVoteType = RefreshRateSelector::LayerVoteType;
+using SetPolicyResult = RefreshRateSelector::SetPolicyResult;
+
+using mock::createDisplayMode;
+
+struct TestableRefreshRateSelector : RefreshRateSelector {
+ using RefreshRateSelector::FrameRateRanking;
+ using RefreshRateSelector::RefreshRateOrder;
+
+ using RefreshRateSelector::RefreshRateSelector;
+
+ void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return RefreshRateSelector::setActiveMode(modeId, renderFrameRate);
+ }
+
+ const DisplayMode& getActiveMode() const {
+ std::lock_guard lock(mLock);
+ return *RefreshRateSelector::getActiveModeLocked().modePtr;
+ }
+
+ ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return ftl::as_non_null(mMinRefreshRateModeIt->second);
+ }
+
+ ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return ftl::as_non_null(mMaxRefreshRateModeIt->second);
+ }
+
+ ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ return ftl::as_non_null(getMinRefreshRateByPolicyLocked());
+ }
+
+ ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ return ftl::as_non_null(
+ getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()));
+ }
+
+ FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder) const {
+ std::lock_guard lock(mLock);
+ return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder);
+ }
+
+ const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
+
+ using RefreshRateSelector::GetRankedFrameRatesCache;
+ auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+
+ auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
+
+ EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
+ ScoredFrameRate::DescendingScore{}));
+
+ return result;
+ }
+
+ auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
+ return std::make_pair(ranking, consideredSignals);
+ }
+
+ ftl::NonNull<DisplayModePtr> getBestFrameRateMode(
+ const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
+ }
+
+ ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {},
+ GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front();
+ }
+
+ SetPolicyResult setPolicy(const PolicyVariant& policy) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return RefreshRateSelector::setPolicy(policy);
+ }
+
+ SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
+ return setPolicy(policy);
+ }
+
+ const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+};
+
+class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
+protected:
+ using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder;
+
+ RefreshRateSelectorTest();
+ ~RefreshRateSelectorTest();
+
+ static constexpr DisplayModeId kModeId60{0};
+ static constexpr DisplayModeId kModeId90{1};
+ static constexpr DisplayModeId kModeId72{2};
+ static constexpr DisplayModeId kModeId120{3};
+ static constexpr DisplayModeId kModeId30{4};
+ static constexpr DisplayModeId kModeId25{5};
+ static constexpr DisplayModeId kModeId50{6};
+ static constexpr DisplayModeId kModeId24{7};
+ static constexpr DisplayModeId kModeId24Frac{8};
+ static constexpr DisplayModeId kModeId30Frac{9};
+ static constexpr DisplayModeId kModeId60Frac{10};
+ static constexpr DisplayModeId kModeId35{11};
+
+ static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+ ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode60Frac =
+ ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90_4K =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}));
+ static inline const ftl::NonNull<DisplayModePtr> kMode72 =
+ ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+ ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode30 =
+ ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode30Frac =
+ ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode25 =
+ ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode35 =
+ ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode50 =
+ ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode24 =
+ ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode24Frac =
+ ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz));
+
+ // Test configurations.
+ static inline const DisplayModes kModes_60 = makeModes(kMode60);
+ static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90);
+ static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
+ static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
+ static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
+ static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72);
+ static inline const DisplayModes kModes_60_90_72_120 =
+ makeModes(kMode60, kMode90, kMode72, kMode120);
+ static inline const DisplayModes kModes_30_60_72_90_120 =
+ makeModes(kMode60, kMode90, kMode72, kMode120, kMode30);
+
+ static inline const DisplayModes kModes_30_60 =
+ makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30);
+ static inline const DisplayModes kModes_30_60_72_90 =
+ makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30);
+ static inline const DisplayModes kModes_30_60_90 =
+ makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30);
+ static inline const DisplayModes kModes_25_30_50_60 =
+ makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
+ static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
+
+ // This is a typical TV configuration.
+ static inline const DisplayModes kModes_24_25_30_50_60_Frac =
+ makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
+ kMode60Frac);
+
+ static TestableRefreshRateSelector createSelector(DisplayModes modes,
+ DisplayModeId activeModeId,
+ Config config = {}) {
+ config.enableFrameRateOverride = GetParam();
+ return TestableRefreshRateSelector(modes, activeModeId, config);
+ }
+};
+
+RefreshRateSelectorTest::RefreshRateSelectorTest() {
+ 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());
+}
+
+RefreshRateSelectorTest::~RefreshRateSelectorTest() {
+ 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());
+}
+
+namespace {
+
+INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest,
+ testing::Values(Config::FrameRateOverride::Disabled,
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+ Config::FrameRateOverride::AppOverride,
+ Config::FrameRateOverride::Enabled));
+
+TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) {
+ auto selector = createSelector(kModes_60, kModeId60);
+ if (GetParam() == Config::FrameRateOverride::Enabled) {
+ EXPECT_TRUE(selector.canSwitch());
+ } else {
+ EXPECT_FALSE(selector.canSwitch());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, invalidPolicy) {
+ auto selector = createSelector(kModes_60, kModeId60);
+
+ EXPECT_EQ(SetPolicyResult::Invalid,
+ selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
+ EXPECT_EQ(SetPolicyResult::Invalid,
+ selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
+}
+
+TEST_P(RefreshRateSelectorTest, unchangedPolicy) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+ EXPECT_EQ(SetPolicyResult::Unchanged,
+ selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+ // Override to the same policy.
+ EXPECT_EQ(SetPolicyResult::Unchanged,
+ selector.setPolicy(RefreshRateSelector::OverridePolicy{kModeId90, {60_Hz, 90_Hz}}));
+
+ // Clear override to restore DisplayManagerPolicy.
+ EXPECT_EQ(SetPolicyResult::Unchanged,
+ selector.setPolicy(RefreshRateSelector::NoOverridePolicy{}));
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ const auto minRate = selector.getMinSupportedRefreshRate();
+ const auto performanceRate = selector.getMaxSupportedRefreshRate();
+
+ EXPECT_EQ(kMode60, minRate);
+ EXPECT_EQ(kMode90, performanceRate);
+
+ const auto minRateByPolicy = selector.getMinRefreshRateByPolicy();
+ const auto performanceRateByPolicy = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(minRateByPolicy, minRate);
+ EXPECT_EQ(performanceRateByPolicy, performanceRate);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ const auto minRate = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate = selector.getMaxSupportedRefreshRate();
+ const auto minRate60 = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(kMode60, minRate);
+ EXPECT_EQ(kMode60, minRate60);
+ EXPECT_EQ(kMode60, performanceRate60);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ const auto minRate90 = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(kMode90_G1, performanceRate);
+ EXPECT_EQ(kMode90_G1, minRate90);
+ EXPECT_EQ(kMode90_G1, performanceRate90);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+ auto selector = createSelector(kModes_60_90_4K, kModeId60);
+
+ const auto minRate = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate = selector.getMaxSupportedRefreshRate();
+ const auto minRate60 = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(kMode60, minRate);
+ EXPECT_EQ(kMode60, minRate60);
+ EXPECT_EQ(kMode60, performanceRate60);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ const auto minRate90 = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(kMode90_4K, performanceRate);
+ EXPECT_EQ(kMode90_4K, minRate90);
+ EXPECT_EQ(kMode90_4K, performanceRate90);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_policyChange) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ const auto minRate = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(kMode60, minRate);
+ EXPECT_EQ(kMode90, performanceRate);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+
+ const auto minRate60 = selector.getMinRefreshRateByPolicy();
+ const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
+
+ EXPECT_EQ(kMode60, minRate60);
+ EXPECT_EQ(kMode60, performanceRate60);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+ {
+ const auto& mode = selector.getActiveMode();
+ EXPECT_EQ(mode.getId(), kModeId60);
+ }
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+ {
+ const auto& mode = selector.getActiveMode();
+ EXPECT_EQ(mode.getId(), kModeId90);
+ }
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+ {
+ const auto& mode = selector.getActiveMode();
+ EXPECT_EQ(mode.getId(), kModeId90);
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) {
+ {
+ auto selector = createSelector(kModes_60_72_90, kModeId72);
+
+ // If there are no layers we select the default frame rate, which is the max of the primary
+ // range.
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode());
+ }
+ {
+ // We select max even when this will cause a non-seamless switch.
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+ constexpr bool kAllowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
+ EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId72);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ layers[0].desiredRefreshRate = 120_Hz;
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ lr.name = "Min";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ lr.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ lr.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ lr.name = "45Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ lr.name = "30Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ lr.name = "24Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.name = "";
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ lr.name = "Min";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ lr.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ lr.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ lr.name = "45Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ lr.name = "30Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ lr.name = "24Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) {
+ auto selector = createSelector(kModes_60_72_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 48_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 48_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "60Hz ExplicitDefault";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.name = "24Hz Heuristic";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.name = "90Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) {
+ auto selector =
+ createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120});
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+ auto& lr3 = layers[2];
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "60Hz ExplicitDefault";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.name = "24Hz Heuristic";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.name = "24Hz ExplicitDefault";
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.name = "90Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.name = "120Hz ExplicitDefault";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 24_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "24Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.vote = LayerVoteType::ExplicitExact;
+ lr2.name = "120Hz ExplicitExact";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 10_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "30Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "120Hz ExplicitExact";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+ lr1.desiredRefreshRate = 30_Hz;
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.name = "30Hz ExplicitExactOrMultiple";
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ lr3.vote = LayerVoteType::Heuristic;
+ lr3.desiredRefreshRate = 120_Hz;
+ lr3.name = "120Hz Heuristic";
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) {
+ auto selector = createSelector(kModes_30_60, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) {
+ auto selector = createSelector(kModes_30_60_72_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Min;
+ lr.name = "Min";
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ lr.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 90_Hz;
+ lr.vote = LayerVoteType::Heuristic;
+ lr.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz Heuristic";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+ lr.desiredRefreshRate = 45_Hz;
+ lr.name = "45Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+ lr.desiredRefreshRate = 30_Hz;
+ lr.name = "30Hz Heuristic";
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+ lr.desiredRefreshRate = 24_Hz;
+ lr.name = "24Hz Heuristic";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+ lr.desiredRefreshRate = 24_Hz;
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.name = "24Hz ExplicitExactOrMultiple";
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) {
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Max;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Min;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Max;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Max;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 15_Hz;
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 30_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = Fps::fromValue(fps);
+ const auto mode = selector.getBestFrameRateMode(layers);
+ EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
+ << to_string(mode->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120});
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = Fps::fromValue(fps);
+ const auto mode = selector.getBestFrameRateMode(layers);
+ EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
+ << to_string(mode->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 90_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 90_Hz;
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = Fps::fromValue(fps);
+ const auto mode = selector.getBestFrameRateMode(layers, {});
+ EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
+ << to_string(mode->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.name = "90Hz ExplicitDefault";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 30_Hz;
+ lr1.name = "30Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 30_Hz;
+ lr1.name = "30Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ // The other layer starts to provide buffers
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 90_Hz;
+ lr2.name = "90Hz Heuristic";
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
+ // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+ // different group.
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
+ const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
+ RefreshRateOrder::Descending);
+
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+ }
+ }();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
+ // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+ // different group.
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
+
+ const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
+ RefreshRateOrder::Ascending);
+
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ }
+ }();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+ // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+ // different group.
+ auto selector = createSelector(kModes_30_60_90, kModeId72);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
+
+ const auto refreshRates =
+ selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
+
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ }
+ }();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+ // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+ // different group.
+ auto selector = createSelector(kModes_30_60_90, kModeId72);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
+
+ const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
+ RefreshRateOrder::Descending);
+
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+ }
+ }();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
+ EXPECT_FALSE(signals.powerOnImminent);
+
+ auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+ }
+ }();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+
+ std::tie(refreshRates, signals) =
+ selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true});
+ EXPECT_TRUE(signals.powerOnImminent);
+
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr1 = layers[0];
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+
+ std::tie(refreshRates, signals) =
+ selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true});
+ EXPECT_TRUE(signals.powerOnImminent);
+
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+
+ std::tie(refreshRates, signals) =
+ selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
+ EXPECT_FALSE(signals.powerOnImminent);
+
+ expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+ }
+ }();
+ ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+ for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, touchConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ auto [_, signals] = selector.getRankedFrameRates({}, {});
+ EXPECT_FALSE(signals.touch);
+
+ std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
+ EXPECT_TRUE(signals.touch);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz Heuristic";
+ std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+ EXPECT_TRUE(signals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitDefault";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz Heuristic";
+ std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+ EXPECT_FALSE(signals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz Heuristic";
+ std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+ EXPECT_TRUE(signals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.desiredRefreshRate = 60_Hz;
+ lr1.name = "60Hz ExplicitDefault";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz Heuristic";
+ std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+ EXPECT_FALSE(signals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
+ auto selector = createSelector(kModes_60_90_72_120, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ // Prepare a table with the vote and the expected refresh rate
+ const std::initializer_list<std::pair<Fps, Fps>> testCases = {
+ {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz},
+
+ {100_Hz, 90_Hz}, {90_Hz, 90_Hz}, {89_Hz, 90_Hz},
+
+ {80_Hz, 72_Hz}, {73_Hz, 72_Hz}, {72_Hz, 72_Hz}, {71_Hz, 72_Hz}, {70_Hz, 72_Hz},
+
+ {65_Hz, 60_Hz}, {60_Hz, 60_Hz}, {59_Hz, 60_Hz}, {58_Hz, 60_Hz},
+
+ {55_Hz, 90_Hz}, {50_Hz, 90_Hz}, {45_Hz, 90_Hz},
+
+ {42_Hz, 120_Hz}, {40_Hz, 120_Hz}, {39_Hz, 120_Hz},
+
+ {37_Hz, 72_Hz}, {36_Hz, 72_Hz}, {35_Hz, 72_Hz},
+
+ {30_Hz, 60_Hz},
+ };
+
+ for (auto [desired, expected] : testCases) {
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = desired;
+
+ std::stringstream ss;
+ ss << "ExplicitDefault " << desired;
+ lr.name = ss.str();
+
+ EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ // Test that 23.976 will choose 24 if 23.976 is not supported
+ {
+ auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+ kMode60Frac),
+ kModeId60);
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 23.976_Hz;
+ lr.name = "ExplicitExactOrMultiple 23.976 Hz";
+ EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
+ }
+
+ // Test that 24 will choose 23.976 if 24 is not supported
+ {
+ auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+ kMode60, kMode60Frac),
+ kModeId60);
+
+ lr.desiredRefreshRate = 24_Hz;
+ lr.name = "ExplicitExactOrMultiple 24 Hz";
+ EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId());
+ }
+
+ // Test that 29.97 will prefer 59.94 over 60 and 30
+ {
+ auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60,
+ kMode60Frac),
+ kModeId60);
+
+ lr.desiredRefreshRate = 29.97_Hz;
+ lr.name = "ExplicitExactOrMultiple 29.97 Hz";
+ EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ // Test that voting for supported refresh rate will select this refresh rate
+ {
+ auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
+
+ for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
+ lr.vote = LayerVoteType::ExplicitExact;
+ lr.desiredRefreshRate = desired;
+ std::stringstream ss;
+ ss << "ExplicitExact " << desired;
+ lr.name = ss.str();
+
+ EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps());
+ }
+ }
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ constexpr FpsRange k90 = {90_Hz, 90_Hz};
+ constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz ExplicitDefault";
+ lr.focused = true;
+
+ const auto [rankedFrameRate, signals] =
+ selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
+
+ EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
+ EXPECT_FALSE(signals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ constexpr FpsRange k60 = {60_Hz, 60_Hz};
+ constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {k60, k60}, {k60_90, k60_90}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 90_Hz;
+ lr.name = "90Hz ExplicitDefault";
+ lr.focused = true;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}));
+}
+
+TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f},
+ {.weight = 1.f},
+ {.weight = 1.f},
+ {.weight = 1.f},
+ {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+ auto& lr3 = layers[2];
+ auto& lr4 = layers[3];
+ auto& lr5 = layers[4];
+
+ lr1.desiredRefreshRate = 90_Hz;
+ lr1.name = "90Hz";
+ lr1.focused = true;
+
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz";
+ lr2.focused = true;
+
+ lr3.desiredRefreshRate = 72_Hz;
+ lr3.name = "72Hz";
+ lr3.focused = true;
+
+ lr4.desiredRefreshRate = 120_Hz;
+ lr4.name = "120Hz";
+ lr4.focused = true;
+
+ lr5.desiredRefreshRate = 30_Hz;
+ lr5.name = "30Hz";
+ lr5.focused = true;
+
+ auto expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+
+ auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+
+ lr1.vote = LayerVoteType::Max;
+ lr1.name = "Max";
+
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz";
+
+ lr3.desiredRefreshRate = 72_Hz;
+ lr3.name = "72Hz";
+
+ lr4.desiredRefreshRate = 90_Hz;
+ lr4.name = "90Hz";
+
+ lr5.desiredRefreshRate = 120_Hz;
+ lr5.name = "120Hz";
+
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+
+ lr1.vote = LayerVoteType::Heuristic;
+ lr1.desiredRefreshRate = 30_Hz;
+ lr1.name = "30Hz";
+
+ lr2.desiredRefreshRate = 120_Hz;
+ lr2.name = "120Hz";
+
+ lr3.desiredRefreshRate = 60_Hz;
+ lr3.name = "60Hz";
+
+ lr5.desiredRefreshRate = 72_Hz;
+ lr5.name = "72Hz";
+
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30},
+ {60_Hz, kMode60},
+ {90_Hz, kMode90},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}, {120_Hz, kMode120},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+
+ lr1.desiredRefreshRate = 120_Hz;
+ lr1.name = "120Hz";
+ lr1.weight = 0.0f;
+
+ lr2.desiredRefreshRate = 60_Hz;
+ lr2.name = "60Hz";
+ lr2.vote = LayerVoteType::NoVote;
+
+ lr3.name = "60Hz-2";
+ lr3.vote = LayerVoteType::Heuristic;
+
+ lr4.vote = LayerVoteType::ExplicitExact;
+
+ lr5.desiredRefreshRate = 120_Hz;
+ lr5.name = "120Hz-2";
+
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90},
+ {60_Hz, kMode60},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {120_Hz, kMode120}, {72_Hz, kMode72},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+
+ ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+ for (size_t i = 0; i < expectedRanking.size(); ++i) {
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+ }
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ constexpr FpsRange k90 = {90_Hz, 90_Hz};
+ constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
+
+ const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+ EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
+ EXPECT_FALSE(signals.touch);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz ExplicitExactOrMultiple";
+ lr.focused = false;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.focused = true;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz ExplicitDefault";
+ lr.focused = false;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.focused = true;
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Heuristic;
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz Heuristic";
+ lr.focused = false;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.focused = true;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Max;
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz Max";
+ lr.focused = false;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.focused = true;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.vote = LayerVoteType::Min;
+ lr.desiredRefreshRate = 60_Hz;
+ lr.name = "60Hz Min";
+ lr.focused = false;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+ lr.focused = true;
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ // The default policy doesn't allow group switching. Verify that no
+ // group switches are performed.
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = 90_Hz;
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
+
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = 90_Hz;
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ // Verify that we won't change the group if seamless switch is required.
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = 90_Hz;
+ layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ // Verify that we won't do a seamless switch if we request the same mode as the default
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = 60_Hz;
+ layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ // Verify that if the active mode is in another group and there are no layers with
+ // Seamlessness::SeamedAndSeamless, we should switch back to the default group.
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = 60_Hz;
+ layer.seamlessness = Seamlessness::Default;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
+
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with
+ // Seamlessness::OnlySeamless can't change the mode group.
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].seamlessness = Seamlessness::OnlySeamless;
+ layers[0].name = "60Hz ExplicitDefault";
+ layers[0].focused = true;
+
+ layers.push_back(LayerRequirement{.weight = 0.5f});
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = 90_Hz;
+ layers[1].name = "90Hz ExplicitDefault";
+ layers[1].focused = false;
+
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with
+ // Seamlessness::Default can't change the mode group back to the group of the default
+ // mode.
+ // For example, this may happen when a video playback requests and gets a seamed switch,
+ // but another layer (with default seamlessness) starts animating. The animating layer
+ // should not cause a seamed switch.
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.1f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = 90_Hz;
+ layers[1].focused = true;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ // Layer with Seamlessness::Default can change the mode group if there's an
+ // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens
+ // when in split screen mode the user switches between the two visible applications.
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.7f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = 90_Hz;
+ layers[1].focused = false;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
+ auto selector = createSelector(kModes_30_60, kModeId60);
+
+ // Allow group switching.
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ layer.desiredRefreshRate = 60_Hz;
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "60Hz ExplicitExactOrMultiple";
+ layer.focused = true;
+
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+
+ selector.setActiveMode(kModeId120, 120_Hz);
+ EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+ auto selector = createSelector(kModes_25_30_50_60, kModeId60);
+
+ // Allow group switching.
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = 60_Hz,
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.5f,
+ .focused = false},
+ {.name = "25Hz ExplicitExactOrMultiple",
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = 25_Hz,
+ .seamlessness = Seamlessness::OnlySeamless,
+ .weight = 1.f,
+ .focused = true}};
+
+ EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId());
+
+ auto& seamedLayer = layers[0];
+ seamedLayer.desiredRefreshRate = 30_Hz;
+ seamedLayer.name = "30Hz ExplicitDefault";
+ selector.setActiveMode(kModeId30, 30_Hz);
+
+ EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId90);
+
+ // Allow group switching.
+ RefreshRateSelector::DisplayManagerPolicy policy;
+ policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+ std::vector<LayerRequirement> layers = {
+ {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
+
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+
+ struct Args {
+ bool touch = false;
+ bool focused = true;
+ };
+
+ // Returns the mode selected by getBestFrameRateMode for a single layer with the given
+ // arguments.
+ const auto getFrameRate = [&](LayerVoteType voteType, Fps fps,
+ Args args = {}) -> DisplayModeId {
+ layers[0].vote = voteType;
+ layers[0].desiredRefreshRate = fps;
+ layers[0].focused = args.focused;
+ return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId();
+ };
+
+ constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
+ constexpr FpsRange k30_90 = {30_Hz, 90_Hz};
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
+
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId());
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+ EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+ EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
+
+ // Unfocused layers are not allowed to override primary range.
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
+ EXPECT_EQ(kModeId60,
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
+
+ // Touch boost should be restricted to the primary range.
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
+
+ // When we're higher than the primary range max due to a layer frame rate setting, touch boost
+ // shouldn't drag us back down to the primary range max.
+ EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
+ EXPECT_EQ(kModeId60,
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+ EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
+}
+
+TEST_P(RefreshRateSelectorTest, idle) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+
+ const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+ bool touchActive) -> DisplayModeId {
+ layers[0].vote = voteType;
+ layers[0].desiredRefreshRate = 90_Hz;
+
+ const auto [ranking, signals] =
+ selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+ // Refresh rate will be chosen by either touch state or idle state.
+ EXPECT_EQ(!touchActive, signals.idle);
+ return ranking.front().frameRateMode.modePtr->getId();
+ };
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
+
+ // Idle should be lower priority than touch boost.
+ {
+ constexpr bool kTouchActive = true;
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId90,
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ }
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+ // Idle should be higher precedence than other layer frame rate considerations.
+ selector.setActiveMode(kModeId90, 90_Hz);
+
+ {
+ constexpr bool kTouchActive = false;
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId60,
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ }
+
+ // Idle should be applied rather than the active mode when there are no layers.
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
+ const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps));
+ const Fps expectedFrameRate = [fps] {
+ if (fps < 26.91f) return 24_Hz;
+ if (fps < 37.51f) return 30_Hz;
+ if (fps < 52.51f) return 45_Hz;
+ if (fps < 66.01f) return 60_Hz;
+ if (fps < 81.01f) return 72_Hz;
+ return 90_Hz;
+ }();
+
+ EXPECT_EQ(expectedFrameRate, knownFrameRate);
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ struct Expectation {
+ Fps fps;
+ ftl::NonNull<DisplayModePtr> mode;
+ };
+
+ const std::initializer_list<Expectation> knownFrameRatesExpectations = {
+ {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90},
+ {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90},
+ };
+
+ // Make sure the test tests all the known frame rate
+ const auto& knownFrameRates = selector.knownFrameRates();
+ const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(),
+ knownFrameRatesExpectations.begin(),
+ [](Fps fps, const Expectation& expected) {
+ return isApproxEqual(fps, expected.fps);
+ });
+ EXPECT_TRUE(equal);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::Heuristic;
+
+ for (const auto& [fps, mode] : knownFrameRatesExpectations) {
+ layer.desiredRefreshRate = fps;
+ EXPECT_EQ(mode, selector.getBestFrameRateMode(layers));
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+ auto& explicitExactLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactLayer.vote = LayerVoteType::ExplicitExact;
+ explicitExactLayer.name = "ExplicitExact";
+ explicitExactLayer.desiredRefreshRate = 30_Hz;
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ selector.getBestScoredFrameRate(layers, {.touch = true})
+ .frameRateMode);
+
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers, {.touch = true})
+ .frameRateMode);
+ }
+
+ explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
+ explicitExactLayer.desiredRefreshRate = 60_Hz;
+
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ }
+
+ explicitExactLayer.desiredRefreshRate = 72_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ explicitExactLayer.desiredRefreshRate = 90_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ explicitExactLayer.desiredRefreshRate = 120_Hz;
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+ using GlobalSignals = RefreshRateSelector::GlobalSignals;
+ const auto args = std::make_pair(std::vector<LayerRequirement>{},
+ GlobalSignals{.touch = true, .idle = true});
+
+ const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
+ {90_Hz, kMode90}}},
+ GlobalSignals{.touch = true}};
+
+ selector.mutableGetRankedRefreshRatesCache() = {args, result};
+
+ EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+ EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+ RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
+
+ const auto result = selector.getRankedFrameRates(layers, globalSignals);
+
+ const auto& cache = selector.mutableGetRankedRefreshRatesCache();
+ ASSERT_TRUE(cache);
+
+ EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
+ EXPECT_EQ(cache->result, result);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) {
+ auto selector = createSelector(kModes_60_120, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+ auto& explicitExactLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+ explicitExactLayer.vote = LayerVoteType::ExplicitExact;
+ explicitExactLayer.name = "ExplicitExact";
+ explicitExactLayer.desiredRefreshRate = 30_Hz;
+
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+ } else {
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}));
+ }
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
+
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) {
+ auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
+ auto& explicitDefaultLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+ explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+ explicitDefaultLayer.name = "ExplicitDefault";
+ explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
+
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+// b/190578904
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+ if (g_noSlowTests) {
+ GTEST_SKIP();
+ }
+
+ const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
+ constexpr int kMaxRefreshRate = 240;
+
+ DisplayModes displayModes;
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ const DisplayModeId modeId(fps);
+ displayModes.try_emplace(modeId,
+ createDisplayMode(modeId,
+ Fps::fromValue(static_cast<float>(fps))));
+ }
+
+ const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+ layers[0].desiredRefreshRate = fps;
+ layers[0].vote = vote;
+ EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
+ << "Failed for " << ftl::enum_string(vote);
+ };
+
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
+ testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+ }
+}
+
+// b/190578904
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) {
+ constexpr DisplayModeId kActiveModeId{0};
+ DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
+ createDisplayMode(DisplayModeId(1), 53_Hz),
+ createDisplayMode(DisplayModeId(2), 55_Hz),
+ createDisplayMode(DisplayModeId(3), 60_Hz));
+
+ const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
+ const auto selector = createSelector(std::move(displayModes), kActiveModeId);
+
+ const std::vector<LayerRequirement> layers = {
+ {
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = 43_Hz,
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ {
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = 53_Hz,
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.41f,
+ },
+ };
+
+ EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps());
+}
+
+TEST_P(RefreshRateSelectorTest, modeComparison) {
+ EXPECT_LT(kMode60->getFps(), kMode90->getFps());
+ EXPECT_GE(kMode60->getFps(), kMode60->getFps());
+ EXPECT_GE(kMode90->getFps(), kMode90->getFps());
+}
+
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) {
+ using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
+
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+}
+
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
+ using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
+
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
+ EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId30);
+
+ const auto frameRate = 30_Hz;
+ Fps displayRefreshRate = selector.getActiveMode().getFps();
+ EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+ selector.setActiveMode(kModeId60, 60_Hz);
+ displayRefreshRate = selector.getActiveMode().getFps();
+ EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+ selector.setActiveMode(kModeId72, 72_Hz);
+ displayRefreshRate = selector.getActiveMode().getFps();
+ EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+ displayRefreshRate = selector.getActiveMode().getFps();
+ EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+ selector.setActiveMode(kModeId120, 120_Hz);
+ displayRefreshRate = selector.getActiveMode().getFps();
+ EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+ selector.setActiveMode(kModeId90, 90_Hz);
+ displayRefreshRate = selector.getActiveMode().getFps();
+ EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
+
+ EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 25_Hz));
+ EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 23.976_Hz));
+ EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(30_Hz, 29.97_Hz));
+ EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz));
+}
+
+TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
+
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
+
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
+
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
+
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
+ EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
+
+ const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
+ for (auto refreshRate : refreshRates) {
+ EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(refreshRate, refreshRate));
+ }
+
+ EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 25_Hz));
+ EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
+ EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::NoVote;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Min;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Max;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Heuristic;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) {
+ if (GetParam() != Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+ {.ownerUid = 5678, .weight = 1.f}};
+
+ layers[0].name = "Test layer 1234";
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ layers[1].name = "Test layer 5678";
+ layers[1].desiredRefreshRate = 30_Hz;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+
+ EXPECT_EQ(2u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+ ASSERT_EQ(1u, frameRateOverrides.count(5678));
+ EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+
+ layers[1].vote = LayerVoteType::Heuristic;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ layers[1].ownerUid = 1234;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+ EXPECT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 30_Hz;
+
+ const auto expetedFps =
+ GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ // The render frame rate cannot be greater than the physical refresh rate
+ {
+ const FpsRange physical = {60_Hz, 60_Hz};
+ const FpsRange render = {60_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Invalid,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ {
+ const FpsRange physical = {0_Hz, 120_Hz};
+ const FpsRange render = {0_Hz, 60_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ const auto expectedMaxMode =
+ GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60;
+ EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+ }
+
+ {
+ const FpsRange physical = {0_Hz, 120_Hz};
+ const FpsRange render = {120_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ {
+ const FpsRange physical = {120_Hz, 120_Hz};
+ const FpsRange render = {60_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId120, {physical, render}, {physical, render}}));
+ }
+
+ layers[0].name = "30Hz";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ EXPECT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+ {
+ const FpsRange physical = {120_Hz, 120_Hz};
+ const FpsRange render = {30_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId120, {physical, render}, {physical, render}}));
+ }
+
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ EXPECT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+
+ {
+ const FpsRange physical = {120_Hz, 120_Hz};
+ const FpsRange render = {30_Hz, 30_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId120, {physical, render}, {physical, render}}));
+ }
+
+ layers[0].name = "60Hz";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ EXPECT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ // [renderRate, refreshRate]
+ const auto expected = []() -> std::vector<std::pair<Fps, Fps>> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, 30_Hz},
+ {60_Hz, 60_Hz},
+ {72_Hz, 72_Hz},
+ {90_Hz, 90_Hz},
+ {120_Hz, 120_Hz}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz},
+ {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}};
+ }
+ }();
+
+ const auto& primaryRefreshRates = selector.getPrimaryFrameRates();
+ ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+
+ for (size_t i = 0; i < expected.size(); i++) {
+ const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
+ EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
+ EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_60_120, kModeId60);
+
+ constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+ constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz};
+
+ constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
+ /*render*/ k0_120Hz};
+
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+ /*render*/ k0_120Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
+
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
+ /*render*/ k0_60Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
+
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+ /*render*/ k0_60Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+
+ const auto expectedRenderRate =
+ GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz;
+
+ layer.name = "30Hz ExplicitDefault";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::ExplicitDefault;
+ EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ layer.name = "30Hz Heuristic";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::Heuristic;
+ EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ layer.name = "30Hz ExplicitExactOrMultiple";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
+ auto selector = createSelector(kModes_35_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+
+ const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+ bool touchActive) -> DisplayModeId {
+ layers[0].vote = voteType;
+ layers[0].desiredRefreshRate = 90_Hz;
+
+ const auto [ranking, signals] =
+ selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+ // Refresh rate will be chosen by either touch state or idle state.
+ EXPECT_EQ(!touchActive, signals.idle);
+ return ranking.front().frameRateMode.modePtr->getId();
+ };
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}}));
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+ // Idle should be higher precedence than other layer frame rate considerations.
+ selector.setActiveMode(kModeId90, 90_Hz);
+ {
+ constexpr bool kTouchActive = false;
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId35,
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ }
+
+ // Idle should be applied rather than the active mode when there are no layers.
+ EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ constexpr Fps inf = Fps::fromValue(std::numeric_limits<float>::infinity());
+
+ using namespace fps_approx_ops;
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, inf}});
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 53e49eb..3ee53c9 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -21,7 +21,7 @@
#include <mutex>
#include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -41,8 +41,6 @@
using MockEventThread = android::mock::EventThread;
using MockLayer = android::mock::MockLayer;
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
-
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
@@ -59,14 +57,31 @@
SchedulerTest();
- static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz);
- static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz);
+ static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
+ ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 =
+ ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
+ static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
- std::shared_ptr<RefreshRateConfigs> mConfigs =
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId());
+ static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
+ ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 =
+ ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz));
+ static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
+
+ static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 =
+ ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz));
+ static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
+
+ std::shared_ptr<RefreshRateSelector> mSelector =
+ std::make_shared<RefreshRateSelector>(makeModes(kDisplay1Mode60),
+ kDisplay1Mode60->getId());
mock::SchedulerCallback mSchedulerCallback;
- TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
+ TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
ConnectionHandle mConnectionHandle;
MockEventThread* mEventThread;
@@ -105,7 +120,7 @@
// The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
- mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
+ mScheduler->onHotplugReceived(handle, kDisplayId1, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
mScheduler->onScreenAcquired(handle);
@@ -129,8 +144,8 @@
ASSERT_EQ(mEventThreadConnection, connection);
EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
- EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
- mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
+ EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1);
+ mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false);
EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
mScheduler->onScreenAcquired(mConnectionHandle);
@@ -166,7 +181,7 @@
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
- EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+ EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
mScheduler->chooseRefreshRateForContent();
}
@@ -175,8 +190,10 @@
sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
ASSERT_EQ(1u, mScheduler->layerHistorySize());
- mScheduler->setRefreshRateConfigs(
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+ // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes.
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -193,14 +210,16 @@
TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setId(DisplayModeId(111))
- .setPhysicalDisplayId(PHYSICAL_DISPLAY_ID)
+ .setPhysicalDisplayId(kDisplayId1)
.setVsyncPeriod(111111)
.build();
// If the handle is incorrect, the function should return before
// onModeChange is called.
ConnectionHandle invalidHandle = {.id = 123};
- EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
+ EXPECT_NO_FATAL_FAILURE(
+ mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+ {90_Hz, ftl::as_non_null(mode)}));
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
}
@@ -215,12 +234,13 @@
}
MATCHER(Is120Hz, "") {
- return isApproxEqual(arg->getFps(), 120_Hz);
+ return isApproxEqual(arg.front().mode.fps, 120_Hz);
}
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
- mScheduler->setRefreshRateConfigs(
- std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
@@ -233,12 +253,146 @@
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
- EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1);
+ EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
mScheduler->chooseRefreshRateForContent();
// No-op if layer requirements have not changed.
- EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+ EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
mScheduler->chooseRefreshRateForContent();
}
+TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
+
+ std::vector<RefreshRateSelector::LayerRequirement> layers =
+ std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+ mScheduler->setContentRequirements(layers);
+ GlobalSignals globalSignals = {.idle = true};
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
+
+ auto modeChoices = mScheduler->chooseDisplayModes();
+ ASSERT_EQ(1u, modeChoices.size());
+
+ auto choice = modeChoices.get(kDisplayId1);
+ ASSERT_TRUE(choice);
+ EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals));
+
+ globalSignals = {.idle = false};
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ modeChoices = mScheduler->chooseDisplayModes();
+ ASSERT_EQ(1u, modeChoices.size());
+
+ choice = modeChoices.get(kDisplayId1);
+ ASSERT_TRUE(choice);
+ EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
+
+ globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ modeChoices = mScheduler->chooseDisplayModes();
+ ASSERT_EQ(1u, modeChoices.size());
+
+ choice = modeChoices.get(kDisplayId1);
+ ASSERT_TRUE(choice);
+ EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
+
+ mScheduler->unregisterDisplay(kDisplayId1);
+ EXPECT_FALSE(mScheduler->hasRefreshRateSelectors());
+}
+
+TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
+ mScheduler->registerDisplay(kDisplayId2,
+ std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+ kDisplay2Mode60->getId()));
+
+ using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
+ TestableScheduler::DisplayModeChoiceMap expectedChoices;
+
+ {
+ const GlobalSignals globalSignals = {.idle = true};
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1,
+ FrameRateMode{60_Hz, kDisplay1Mode60},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{60_Hz,
+ kDisplay2Mode60},
+ globalSignals);
+
+ std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
+ {.weight = 1.f}};
+ mScheduler->setContentRequirements(layers);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
+ }
+ {
+ const GlobalSignals globalSignals = {.idle = false};
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1,
+ FrameRateMode{120_Hz, kDisplay1Mode120},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{120_Hz,
+ kDisplay2Mode120},
+ globalSignals);
+
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
+ }
+ {
+ const GlobalSignals globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ expectedChoices =
+ ftl::init::map<const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1,
+ FrameRateMode{120_Hz, kDisplay1Mode120},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{120_Hz,
+ kDisplay2Mode120},
+ globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
+ }
+ {
+ // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal.
+ mScheduler
+ ->registerDisplay(kDisplayId3,
+ std::make_shared<RefreshRateSelector>(kDisplay3Modes,
+ kDisplay3Mode60->getId()));
+
+ const GlobalSignals globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ expectedChoices = ftl::init::map<
+ const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{60_Hz, kDisplay2Mode60},
+ globalSignals)(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
+ }
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index dfcfd91..6adcd52 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -383,8 +383,8 @@
history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- const auto configs = mFlinger.mutableScheduler().refreshRateConfigs();
- const auto summary = history.summarize(*configs, 0);
+ const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
+ const auto summary = history.summarize(*selectorPtr, 0);
ASSERT_EQ(2u, summary.size());
EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index f7d34ac..6a9c970 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -30,9 +30,6 @@
// --------------------------------------------------------------------
// Call Expectations
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
// --------------------------------------------------------------------
// Invocation
@@ -61,9 +58,6 @@
// --------------------------------------------------------------------
// Call Expectations
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
// --------------------------------------------------------------------
// Invocation
int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index 40ef949..93a3811 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -37,9 +37,6 @@
// --------------------------------------------------------------------
// Call Expectations
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
// Destroying the display commits a display transaction.
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 6b7e353..074bf8c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "mock/MockDisplayModeSpecs.h"
#include "mock/MockEventThread.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -42,15 +43,15 @@
PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
- auto configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+ auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
- setupScheduler(configs);
+ setupScheduler(selectorPtr);
mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
mFlinger.configureAndCommit();
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(std::move(modes), kModeId60, std::move(configs))
+ .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr))
.inject();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
@@ -60,7 +61,7 @@
}
protected:
- void setupScheduler(std::shared_ptr<scheduler::RefreshRateConfigs>);
+ void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
sp<DisplayDevice> mDisplay;
mock::EventThread* mAppEventThread;
@@ -80,7 +81,7 @@
};
void DisplayModeSwitchingTest::setupScheduler(
- std::shared_ptr<scheduler::RefreshRateConfigs> configs) {
+ std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr) {
auto eventThread = std::make_unique<mock::EventThread>();
mAppEventThread = eventThread.get();
auto sfEventThread = std::make_unique<mock::EventThread>();
@@ -108,23 +109,24 @@
mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
std::move(eventThread), std::move(sfEventThread),
TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
- std::move(configs));
+ std::move(selectorPtr));
}
TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(mDisplay);
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
- false, 0.f, 120.f, 0.f, 120.f);
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
+ 120));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -137,17 +139,18 @@
Mock::VerifyAndClearExpectations(mComposer);
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
@@ -157,12 +160,13 @@
mFlinger.onActiveDisplayChanged(mDisplay);
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
- true, 0.f, 120.f, 0.f, 120.f);
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
+ 120));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -172,12 +176,13 @@
hal::HWConfigId(kModeId90.value()), _, _))
.WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -187,12 +192,13 @@
// is still being processed the later call will be respected.
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(mDisplay);
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
- false, 0.f, 120.f, 0.f, 120.f);
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
+ 120));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_CALL(*mComposer,
@@ -202,11 +208,12 @@
mFlinger.commit();
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId120.value(),
- false, 0.f, 180.f, 0.f, 180.f);
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId120.value(), false, 0,
+ 180));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
EXPECT_CALL(*mComposer,
setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
@@ -216,28 +223,29 @@
mFlinger.commit();
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
mFlinger.commit();
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
}
TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(mDisplay);
- mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90_4K.value(),
- false, 0.f, 120.f, 0.f, 120.f);
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
+ 120));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -272,7 +280,7 @@
mDisplay = mFlinger.getDisplay(displayToken);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 73f654b..94d517a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -90,15 +90,12 @@
Case::HdrSupport::setupComposerCallExpectations(this);
Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
expectHotplugReceived<Case, true>(mEventThread);
expectHotplugReceived<Case, true>(mSFEventThread);
}
template <typename Case>
void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
expectHotplugReceived<Case, false>(mEventThread);
expectHotplugReceived<Case, false>(mSFEventThread);
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
new file mode 100644
index 0000000..0e149d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 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 {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class ExcludeDolbyVisionTest : public DisplayTransactionTest {
+public:
+ void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) {
+ DisplayModes modes;
+ for (DisplayModePtr displayMode : displayModePtrs) {
+ modes.try_emplace(displayMode->getId(), displayMode);
+ }
+
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setDisplayModes(std::move(modes), displayModePtrs[0]->getId())
+ .inject();
+ mDisplay->overrideHdrTypes(types);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+
+ static constexpr DisplayModeId modeId1080p60{0};
+ static constexpr DisplayModeId modeId4k30{1};
+ static constexpr DisplayModeId modeId4k60{2};
+
+ static inline const DisplayModePtr mode1080p60 =
+ createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080));
+ static inline const DisplayModePtr mode4k30 =
+ createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k30NonStandard =
+ createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k60 =
+ createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160));
+
+ const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30,
+ ui::Hdr::HDR10_PLUS};
+};
+
+TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(1, displayModes.size());
+ ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(),
+ displayModes[0].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1);
+}
+
+TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) {
+ injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(2, displayModes.size());
+ for (size_t i = 0; i < displayModes.size(); i++) {
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+ ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2);
+ }
+}
+
+TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes();
+
+ ASSERT_EQ(2, displayHdrTypes.size());
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index ec7e8a7..4e9f293 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -21,6 +21,7 @@
#include <thread>
#include "DisplayTransactionTestHelpers.h"
+#include "FakeDisplayInjector.h"
#include <android/hardware/power/Boost.h>
@@ -32,6 +33,8 @@
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
using namespace std::chrono_literals;
+ injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {});
+
mFlinger.scheduler()->replaceTouchTimer(100);
std::this_thread::sleep_for(10ms); // wait for callback to be triggered
EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index 98249bf..f553a23 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -38,11 +38,6 @@
// --------------------------------------------------------------------
// Call Expectations
- // We expect the surface interceptor to possibly be used, but we treat it as
- // disabled since it is called as a side effect rather than directly by this
- // function.
- EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
// We expect a call to get the active display config.
Case::Display::setupHwcGetActiveConfigCallExpectations(this);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index e256d2c..622717f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,6 @@
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
- mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 9e54083..0fb8e2b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -259,12 +259,6 @@
auto injector = Display::makeFakeExistingDisplayInjector(test);
const auto display = injector.inject();
display->setPowerMode(mode);
- if (injector.physicalDisplay()
- .transform(&display::PhysicalDisplay::isInternal)
- .value_or(false)) {
- test->mFlinger.mutableActiveDisplayToken() = display->getDisplayToken();
- }
-
return display;
}
@@ -276,13 +270,6 @@
EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
}
- static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
- PowerMode mode) {
- EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
- EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
- .Times(1);
- }
-
static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
// Any calls to get the active config will return a default value.
EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
@@ -349,7 +336,6 @@
// --------------------------------------------------------------------
// Call Expectations
- Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
Case::Transition::template setupCallExpectations<Case>(this);
// --------------------------------------------------------------------
@@ -498,5 +484,37 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
+TEST_F(SetPowerModeInternalTest, designatesLeaderDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Inject a primary display with uninitialized power mode.
+ constexpr bool kInitPowerMode = false;
+ Case::Display::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ const auto display = injector.inject();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
+ // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
+ // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
+ constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
+ ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
+ mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
+
+ mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should be designated as the leader.
+ EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 073c459..c0796df 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -36,16 +36,13 @@
static constexpr bool WIDE_COLOR_SUPPORTED = true;
static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableHasWideColorDisplay() = true;
+ test->mFlinger.mutableSupportsWideColor() = true;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
}
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
- EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
- Return(Error::NONE)));
EXPECT_CALL(*test->mComposer,
getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
.WillOnce(DoAll(SetArgPointee<2>(
@@ -253,9 +250,15 @@
.hwcDisplayId = *hwcDisplayId,
.activeMode = activeMode};
+ ui::ColorModes colorModes;
+ if constexpr (Case::WideColorSupport::WIDE_COLOR_SUPPORTED) {
+ colorModes.push_back(ColorMode::DISPLAY_P3);
+ }
+
mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId,
*connectionType,
- makeModes(activeMode), std::nullopt);
+ makeModes(activeMode),
+ std::move(colorModes), std::nullopt);
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -285,7 +288,7 @@
if constexpr (Case::Display::CONNECTION_TYPE::value) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId());
+ EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
}
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
index 0cf3bdf..fed6a1a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -44,9 +44,11 @@
std::move(eventThread), std::move(sfEventThread));
}
- sp<Layer> createLayer(const char* name, LayerMetadata layerMetadata) {
- return sp<Layer>::make(
- LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, layerMetadata});
+ sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) {
+ LayerCreationArgs args =
+ LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata};
+ inOutlayerMetadata = args.metadata;
+ return sp<Layer>::make(args);
}
TestableSurfaceFlinger mFlinger;
@@ -90,7 +92,7 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
+ EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
}
// Test that snapshot layer metadata is set by merging the child's metadata on top of its
@@ -109,10 +111,10 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
+ EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
auto expectedChildMetadata =
LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
- ASSERT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
+ EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
}
// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
@@ -129,8 +131,8 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+ EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+ EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
}
// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
@@ -154,7 +156,8 @@
.build();
auto layerB = createLayer("layer-b", layerBMetadata);
auto layerBHandle = layerB->getHandle();
- auto layerC = createLayer("layer-c", {});
+ LayerMetadata layerCMetadata;
+ auto layerC = createLayer("layer-c", layerCMetadata);
auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
auto layerD = createLayer("layer-d", layerDMetadata);
auto layerDHandle = layerD->getHandle();
@@ -168,14 +171,18 @@
mFlinger.updateLayerMetadataSnapshot();
- auto expectedLayerDRelativeMetadata = LayerMetadataBuilder()
- // From layer A, parent of relative parent
- .setInt32(METADATA_OWNER_UID, 1)
- // From layer B, relative parent
- .setInt32(METADATA_TASK_ID, 2)
- .setInt32(METADATA_OWNER_PID, 3)
- .build();
- ASSERT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
+ auto expectedLayerDRelativeMetadata =
+ LayerMetadataBuilder()
+ // From layer A, parent of relative parent
+ .setInt32(METADATA_OWNER_UID, 1)
+ // From layer B, relative parent
+ .setInt32(METADATA_TASK_ID, 2)
+ .setInt32(METADATA_OWNER_PID, 3)
+ // added by layer creation args
+ .setInt32(gui::METADATA_CALLING_UID,
+ layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
+ .build();
+ EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
auto expectedLayerCRelativeMetadata =
LayerMetadataBuilder()
// From layer A, parent of relative parent
@@ -184,8 +191,11 @@
.setInt32(METADATA_OWNER_PID, 3)
// From layer D, relative parent
.setInt32(METADATA_TASK_ID, 4)
+ // added by layer creation args
+ .setInt32(gui::METADATA_CALLING_UID,
+ layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
.build();
- ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
+ EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
}
TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
@@ -193,8 +203,10 @@
auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
auto layerA = createLayer("layer-a", layerAMetadata);
auto layerAHandle = layerA->getHandle();
- auto layerB = createLayer("layer-b", {});
- auto layerC = createLayer("layer-c", {});
+ LayerMetadata layerBMetadata;
+ auto layerB = createLayer("layer-b", layerBMetadata);
+ LayerMetadata layerCMetadata;
+ auto layerC = createLayer("layer-c", layerCMetadata);
layerB->setRelativeLayer(layerAHandle, 1);
layerC->setRelativeLayer(layerAHandle, 2);
layerA->commitChildList();
@@ -204,9 +216,9 @@
mFlinger.updateLayerMetadataSnapshot();
- ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
- ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+ EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+ EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+ EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 93e3059..b8a6063 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -17,6 +17,7 @@
#pragma once
#include <Scheduler/Scheduler.h>
+#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
#include <gui/ISurfaceComposer.h>
@@ -32,17 +33,19 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+ TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_unique<mock::VSyncTracker>(), std::move(configs),
+ std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr),
callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
- std::unique_ptr<VSyncTracker> tracker,
- std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+ std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
+ ISchedulerCallback& callback)
: Scheduler(*this, callback, Feature::kContentDetection) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
- setRefreshRateConfigs(std::move(configs));
+
+ const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
+ registerDisplay(displayId, std::move(selectorPtr));
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
// Execute task to prevent broken promise exception on destruction.
@@ -66,14 +69,39 @@
auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+ auto refreshRateSelector() { return leaderSelectorPtr(); }
+
+ const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
+ return mRefreshRateSelectors;
+ }
+
+ bool hasRefreshRateSelectors() const { return !refreshRateSelectors().empty(); }
+
+ void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ Scheduler::registerDisplay(displayId, std::move(selectorPtr));
+ }
+
+ void unregisterDisplay(PhysicalDisplayId displayId) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ Scheduler::unregisterDisplay(displayId);
+ }
+
+ std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+ return mLeaderDisplayId;
+ }
+
+ void setLeaderDisplay(PhysicalDisplayId displayId) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ Scheduler::setLeaderDisplay(displayId);
+ }
+
auto& mutableLayerHistory() { return mLayerHistory; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
}
- auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
-
size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
return mLayerHistory.mActiveLayerInfos.size();
}
@@ -94,9 +122,27 @@
return mPolicy.touch == Scheduler::TouchState::Active;
}
+ void setTouchStateAndIdleTimerPolicy(GlobalSignals globalSignals) {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.touch = globalSignals.touch ? TouchState::Active : TouchState::Inactive;
+ mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
+ }
+
+ void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.contentRequirements = std::move(layers);
+ }
+
+ using Scheduler::DisplayModeChoice;
+ using Scheduler::DisplayModeChoiceMap;
+
+ DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS {
+ return Scheduler::chooseDisplayModes();
+ }
+
void dispatchCachedReportedMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
- return Scheduler::dispatchCachedReportedMode();
+ Scheduler::dispatchCachedReportedMode();
}
void clearCachedReportedMode() {
@@ -104,8 +150,8 @@
mPolicy.cachedModeChangedParams.reset();
}
- void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
- return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
+ Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 13389a1..2117084 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,17 +30,20 @@
#include <ftl/fake_guard.h>
#include <gui/ScreenCaptureResults.h>
+#include <ui/DynamicDisplayInfo.h>
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
+#include "RenderArea.h"
#include "Scheduler/MessageQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlinger.h"
#include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -81,10 +84,6 @@
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- sp<SurfaceInterceptor> createSurfaceInterceptor() override {
- return sp<android::impl::SurfaceInterceptor>::make();
- }
-
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
return sp<StartPropertySetThread>::make(timestampPropertyValue);
}
@@ -123,6 +122,10 @@
sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
+ sp<LayerFE> createLayerFE(const std::string& layerName) override {
+ return sp<LayerFE>::make(layerName);
+ }
+
std::unique_ptr<FrameTracer> createFrameTracer() override {
return std::make_unique<mock::FrameTracer>();
}
@@ -158,7 +161,6 @@
if (!mFlinger) {
mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
}
- mFlinger->mAnimationTransactionTimeout = ms2ns(10);
}
SurfaceFlinger* flinger() { return mFlinger.get(); }
@@ -168,7 +170,8 @@
// functions.
void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
- mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+ mFlinger->mRenderEngine = std::move(renderEngine);
+ mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
}
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
@@ -192,10 +195,10 @@
static constexpr struct TwoDisplayModes {
} kTwoDisplayModes;
- using RefreshRateConfigsPtr = std::shared_ptr<scheduler::RefreshRateConfigs>;
+ using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
using DisplayModesVariant =
- std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateConfigsPtr>;
+ std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateSelectorPtr>;
void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
@@ -204,9 +207,9 @@
SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
DisplayModesVariant modesVariant = kOneDisplayMode,
bool useNiceMock = false) {
- RefreshRateConfigsPtr configs;
- if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) {
- configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant));
+ RefreshRateSelectorPtr selectorPtr;
+ if (std::holds_alternative<RefreshRateSelectorPtr>(modesVariant)) {
+ selectorPtr = std::move(std::get<RefreshRateSelectorPtr>(modesVariant));
} else {
constexpr DisplayModeId kModeId60{0};
DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
@@ -216,10 +219,10 @@
modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
}
- configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
+ selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
}
- const auto fps = FTL_FAKE_GUARD(kMainThreadContext, configs->getActiveMode().getFps());
+ const auto fps = selectorPtr->getActiveMode().fps;
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -237,12 +240,12 @@
mScheduler =
new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
std::move(vsyncTracker),
- std::move(configs),
+ std::move(selectorPtr),
callback);
} else {
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker),
- std::move(configs), callback);
+ std::move(selectorPtr), callback);
}
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
@@ -398,14 +401,15 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto renderScreenImpl(const RenderArea& renderArea,
- SurfaceFlinger::TraverseLayersFunction traverseLayers,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- bool forSystem, bool regionSampling) {
+ auto renderScreenImpl(std::unique_ptr<RenderArea> renderArea,
+ SurfaceFlinger::TraverseLayersFunction traverseLayers,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool forSystem, bool regionSampling) {
ScreenCaptureResults captureResults;
- return mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, forSystem,
- regionSampling, false /* grayscale */,
- captureResults);
+ return FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers,
+ buffer, forSystem, regionSampling,
+ false /* grayscale */, captureResults));
}
auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
@@ -418,22 +422,32 @@
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
- auto& getTransactionQueue() { return mFlinger->mLocklessTransactionQueue; }
- auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
- auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
+ auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+ auto& getPendingTransactionQueue() {
+ return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+ }
+ size_t getPendingTransactionCount() {
+ return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
+ }
- auto setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+ auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ Vector<ComposerState>& states, const Vector<DisplayState>& displays,
+ uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) {
return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
inputWindowCommands, desiredPresentTime,
isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
listenerCallbacks, transactionId);
}
+ auto setTransactionStateInternal(TransactionState& transaction) {
+ return mFlinger->mTransactionHandler.queueTransaction(std::move(transaction));
+ }
+
auto flushTransactionQueues() {
return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues(kVsyncId));
}
@@ -449,36 +463,35 @@
return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
- auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
- bool allowGroupSwitching, float primaryRefreshRateMin,
- float primaryRefreshRateMax, float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
- return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
- primaryRefreshRateMin, primaryRefreshRateMax,
- appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+ const gui::DisplayModeSpecs& specs) {
+ return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
}
void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
- mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+ mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
}
- auto createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
- const sp<IBinder>& parentHandle, int32_t* outLayerId,
- const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
- return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, parentLayer,
- outTransformHint);
+ auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
+ gui::CreateSurfaceResult& outResult) {
+ args.parentHandle = parentHandle;
+ return mFlinger->createLayer(args, outResult);
}
auto mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle, int32_t* outLayerId) {
- return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
+ gui::CreateSurfaceResult& outResult) {
+ return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult);
}
void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+ void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* dynamicDisplayInfo) {
+ mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -493,10 +506,6 @@
return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
}
- nsecs_t getAnimationTransactionTimeout() const {
- return mFlinger->mAnimationTransactionTimeout;
- }
-
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -511,7 +520,7 @@
const auto& hwcPhysicalDisplayIdMap() const { return getHwComposer().mPhysicalDisplayIdMap; }
const auto& hwcDisplayData() const { return getHwComposer().mDisplayData; }
- auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
+ auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
auto& mutableCurrentState() { return mFlinger->mCurrentState; }
auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
@@ -519,7 +528,6 @@
auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
- auto& mutableInterceptor() { return mFlinger->mInterceptor; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
auto& mutableTexturePool() { return mFlinger->mTexturePool; }
@@ -530,11 +538,9 @@
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
- auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
+ auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
- auto fromHandle(const sp<IBinder>& handle) {
- return mFlinger->fromHandle(handle);
- }
+ auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
@@ -544,11 +550,10 @@
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
- mutableInterceptor().clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
- mFlinger->mCompositionEngine->setRenderEngine(
- std::unique_ptr<renderengine::RenderEngine>());
+ mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
+ mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
}
/* ------------------------------------------------------------------------
@@ -622,7 +627,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mPowerMode = mode;
return *this;
}
@@ -644,16 +649,18 @@
// is much longer lived.
auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
mHwcDisplayType);
-
display->mutableIsConnected() = true;
- display->setPowerMode(mPowerMode);
+
+ if (mPowerMode) {
+ display->setPowerMode(*mPowerMode);
+ }
+
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
.WillRepeatedly(
DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
Return(hal::Error::NONE)));
-
EXPECT_CALL(*composer,
getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
@@ -712,7 +719,7 @@
int32_t mDpiY = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
- hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+ std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
mCapabilities = nullptr;
};
@@ -760,16 +767,17 @@
return mFlinger.mutableDisplays().get(mDisplayToken)->get();
}
- // If `configs` is nullptr, the injector creates RefreshRateConfigs from the `modes`.
- // Otherwise, it uses `configs`, which the caller must create using the same `modes`.
+ // If `selectorPtr` is nullptr, the injector creates RefreshRateSelector from the `modes`.
+ // Otherwise, it uses `selectorPtr`, which the caller must create using the same `modes`.
//
- // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateConfigs, remove
- // the `configs` parameter in favor of an alternative setRefreshRateConfigs API.
- auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId,
- std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) {
+ // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateSelector, remove
+ // the `selectorPtr` parameter in favor of an alternative setRefreshRateSelector API.
+ auto& setDisplayModes(
+ DisplayModes modes, DisplayModeId activeModeId,
+ std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr = nullptr) {
mDisplayModes = std::move(modes);
mCreationArgs.activeModeId = activeModeId;
- mCreationArgs.refreshRateConfigs = std::move(configs);
+ mCreationArgs.refreshRateSelector = std::move(selectorPtr);
return *this;
}
@@ -788,7 +796,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mCreationArgs.initialPowerMode = mode;
return *this;
}
@@ -816,7 +824,7 @@
auto& modes = mDisplayModes;
auto& activeModeId = mCreationArgs.activeModeId;
- if (displayId && !mCreationArgs.refreshRateConfigs) {
+ if (displayId && !mCreationArgs.refreshRateSelector) {
if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
if (modes.empty()) {
constexpr DisplayModeId kModeId{0};
@@ -836,8 +844,8 @@
activeModeId = kModeId;
}
- mCreationArgs.refreshRateConfigs =
- std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+ mCreationArgs.refreshRateSelector =
+ std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId);
}
}
@@ -849,24 +857,36 @@
if (mConnectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
- const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
- LOG_ALWAYS_FATAL_IF(!physicalId);
+ const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
+ LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
+ const auto physicalId = *physicalIdOpt;
+
+ if (mCreationArgs.isPrimary) {
+ mFlinger.mutableActiveDisplayId() = physicalId;
+ }
+
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
const auto activeMode = modes.get(activeModeId);
LOG_ALWAYS_FATAL_IF(!activeMode);
+ const auto fps = activeMode->get()->getFps();
- state.physical = {.id = *physicalId,
+ state.physical = {.id = physicalId,
.hwcDisplayId = *mHwcDisplayId,
.activeMode = activeMode->get()};
- const auto it = mFlinger.mutablePhysicalDisplays()
- .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
- *mConnectionType, std::move(modes),
- std::nullopt)
- .first;
+ mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
+ physicalId, *mConnectionType,
+ std::move(modes),
+ ui::ColorModes(),
+ std::nullopt);
- display->setActiveMode(activeModeId, it->second.snapshot());
+ if (mFlinger.scheduler()) {
+ mFlinger.scheduler()->registerDisplay(physicalId,
+ display->holdRefreshRateSelector());
+ }
+
+ display->setActiveMode(activeModeId, fps, fps);
}
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index d5a5bae..a9ae1d3 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -1142,8 +1142,10 @@
kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1277,8 +1279,10 @@
GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1363,16 +1367,19 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
// The first time record is never uploaded to stats.
- ASSERT_EQ(atomList.atom_size(), 3);
+ ASSERT_EQ(atomList.atom_size(), 4);
// Layers are ordered based on the hash in LayerStatsKey. For this test, the order happens to
- // be: 0 - Battery 1 - Performance 2 - Standard
+ // be: 0 - Battery 1 - Custom 2 - Performance 3 - Standard
const SurfaceflingerStatsLayerInfo& atom0 = atomList.atom(0);
EXPECT_EQ(atom0.layer_name(), genLayerName(LAYER_ID_0));
@@ -1407,7 +1414,7 @@
EXPECT_EQ(atom1.uid(), UID_0);
EXPECT_EQ(atom1.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
EXPECT_EQ(atom1.render_rate_bucket(), RENDER_RATE_BUCKET_0);
- EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+ EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM);
const SurfaceflingerStatsLayerInfo& atom2 = atomList.atom(2);
@@ -1420,12 +1427,30 @@
EXPECT_THAT(atom2.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
EXPECT_THAT(atom2.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
EXPECT_THAT(atom2.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
- EXPECT_EQ(atom2.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
- EXPECT_EQ(atom2.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom2.late_acquire_frames(), 0);
+ EXPECT_EQ(atom2.bad_desired_present_frames(), 0);
EXPECT_EQ(atom2.uid(), UID_0);
EXPECT_EQ(atom2.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
EXPECT_EQ(atom2.render_rate_bucket(), RENDER_RATE_BUCKET_0);
- EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
+ EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+
+ const SurfaceflingerStatsLayerInfo& atom3 = atomList.atom(3);
+
+ EXPECT_EQ(atom3.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom3.total_frames(), 1);
+ EXPECT_EQ(atom3.dropped_frames(), 0);
+ EXPECT_THAT(atom3.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom3.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+ EXPECT_THAT(atom3.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+ EXPECT_THAT(atom3.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom3.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom3.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_EQ(atom3.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+ EXPECT_EQ(atom3.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom3.uid(), UID_0);
+ EXPECT_EQ(atom3.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom3.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_EQ(atom3.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
@@ -1436,8 +1461,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1461,8 +1488,10 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1480,8 +1509,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1500,8 +1531,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index b493d11..d84698f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#undef LOG_TAG
#define LOG_TAG "CompositionTest"
@@ -22,12 +21,18 @@
#include <compositionengine/mock/DisplaySurface.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <gui/LayerState.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/fake/BufferData.h>
#include <log/log.h>
#include <ui/MockFence.h>
#include <utils/String8.h>
+#include <vector>
+#include <binder/Binder.h>
+#include "FrontEnd/TransactionHandler.h"
#include "TestableSurfaceFlinger.h"
+#include "TransactionState.h"
#include "mock/MockEventThread.h"
#include "mock/MockVsyncController.h"
@@ -37,7 +42,9 @@
using testing::Return;
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using frontend::TransactionHandler;
+constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
class TransactionApplicationTest : public testing::Test {
public:
TransactionApplicationTest() {
@@ -78,6 +85,7 @@
mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
std::move(eventThread), std::move(sfEventThread));
+ mFlinger.flinger()->addTransactionReadyFilters();
}
TestableSurfaceFlinger mFlinger;
@@ -112,7 +120,7 @@
void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
mTransactionNumber++;
- transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
+ transaction.flags |= flags;
transaction.desiredPresentTime = desiredPresentTime;
transaction.isAutoTimestamp = isAutoTimestamp;
transaction.frameTimelineInfo = frameTimelineInfo;
@@ -136,11 +144,7 @@
// If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
// SF to commit the transaction. If this is animation, it should not time out waiting.
nsecs_t returnedTime = systemTime();
- if (flags & ISurfaceComposer::eSynchronous) {
- EXPECT_GE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
- } else {
- EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
- }
+ EXPECT_LE(returnedTime, applicationTime + TRANSACTION_TIMEOUT);
// Each transaction should have been placed on the transaction queue
auto& transactionQueue = mFlinger.getTransactionQueue();
EXPECT_FALSE(transactionQueue.isEmpty());
@@ -165,13 +169,7 @@
transaction.id);
nsecs_t returnedTime = systemTime();
- if (flags & ISurfaceComposer::eSynchronous) {
- EXPECT_GE(systemTime(),
- applicationSentTime + mFlinger.getAnimationTransactionTimeout());
- } else {
- EXPECT_LE(returnedTime,
- applicationSentTime + mFlinger.getAnimationTransactionTimeout());
- }
+ EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
// This transaction should have been placed on the transaction queue
auto& transactionQueue = mFlinger.getTransactionQueue();
EXPECT_FALSE(transactionQueue.isEmpty());
@@ -204,7 +202,7 @@
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
// commit the transaction)
- EXPECT_LE(systemTime(), applicationSentTime + mFlinger.getAnimationTransactionTimeout());
+ EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT);
// transaction that would goes to pending transaciton queue.
mFlinger.flushTransactionQueues();
@@ -220,13 +218,7 @@
// if this is an animation, this thread should be blocked for 5s
// in setTransactionState waiting for transactionA to flush. Otherwise,
// the transaction should be placed on the pending queue
- if (flags & ISurfaceComposer::eSynchronous) {
- EXPECT_GE(systemTime(),
- applicationSentTime + mFlinger.getAnimationTransactionTimeout());
- } else {
- EXPECT_LE(systemTime(),
- applicationSentTime + mFlinger.getAnimationTransactionTimeout());
- }
+ EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT);
// transaction that would goes to pending transaciton queue.
mFlinger.flushTransactionQueues();
@@ -294,30 +286,18 @@
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
}
-TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
- NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous);
-}
-
TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
NotPlacedOnTransactionQueue(/*flags*/ 0);
}
-TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) {
- PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous);
-}
-
TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
PlaceOnTransactionQueue(/*flags*/ 0);
}
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) {
- BlockedByPriorTransaction(ISurfaceComposer::eSynchronous);
-}
-
TEST_F(TransactionApplicationTest, FromHandle) {
sp<IBinder> badHandle;
auto ret = mFlinger.fromHandle(badHandle);
- EXPECT_EQ(nullptr, ret.promote().get());
+ EXPECT_EQ(nullptr, ret.get());
}
class LatchUnsignaledTest : public TransactionApplicationTest {
@@ -329,7 +309,6 @@
mFlinger.getTransactionQueue().pop();
}
mFlinger.getPendingTransactionQueue().clear();
- mFlinger.getTransactionCommittedSignals().clear();
mFlinger.commitTransactionsLocked(eTransactionMask);
mFlinger.mutableCurrentState().layersSortedByZ.clear();
mFlinger.mutableDrawingState().layersSortedByZ.clear();
@@ -343,7 +322,10 @@
ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
ComposerState state;
- state.state.bufferData = std::make_shared<BufferData>();
+ state.state.bufferData =
+ std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
+ /* height */ 2, /* pixelFormat */ 0,
+ /* outUsage */ 0);
state.state.bufferData->acquireFence = std::move(fence);
state.state.layerId = layerId;
state.state.surface =
@@ -361,7 +343,7 @@
TransactionInfo createTransactionInfo(const sp<IBinder>& applyToken,
const std::vector<ComposerState>& states) {
TransactionInfo transaction;
- const uint32_t kFlags = ISurfaceComposer::eSynchronous;
+ const uint32_t kFlags = 0;
const nsecs_t kDesiredPresentTime = systemTime();
const bool kIsAutoTimestamp = true;
const auto kFrameTimelineInfo = FrameTimelineInfo{};
@@ -376,23 +358,31 @@
}
void setTransactionStates(const std::vector<TransactionInfo>& transactions,
- size_t expectedTransactionsApplied,
size_t expectedTransactionsPending) {
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
- for (const auto& transaction : transactions) {
- mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime,
- transaction.isAutoTimestamp, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks, transaction.id);
+ for (auto transaction : transactions) {
+ std::vector<ResolvedComposerState> resolvedStates;
+ resolvedStates.reserve(transaction.states.size());
+ for (auto& state : transaction.states) {
+ resolvedStates.emplace_back(std::move(state));
+ }
+
+ TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
+ transaction.displays, transaction.flags,
+ transaction.applyToken,
+ transaction.inputWindowCommands,
+ transaction.desiredPresentTime,
+ transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, systemTime(), 0,
+ mHasListenerCallbacks, mCallbacks, getpid(),
+ static_cast<int>(getuid()), transaction.id);
+ mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
- EXPECT_EQ(expectedTransactionsApplied, mFlinger.getTransactionCommittedSignals().size());
+ EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionCount());
}
};
@@ -408,22 +398,19 @@
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
createTransactionInfo(kApplyToken,
{createComposerState(kLayerId, fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged)});
- setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleUnSignaledFromTheQueue) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto unsignaledTransaction =
@@ -433,33 +420,13 @@
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 0u;
- const auto kExpectedTransactionsPending = 1u;
-
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken,
- {
- createComposerState(kLayerId,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eCropChanged),
- });
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
- const sp<IBinder> kApplyToken =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -471,15 +438,31 @@
layer_state_t::
eBufferChanged),
});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
+ const sp<IBinder> kApplyToken =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ const auto kLayerId = 1;
+ const auto kExpectedTransactionsPending = 1u;
+
+ const auto unsignaledTransaction =
+ createTransactionInfo(kApplyToken,
+ {
+ createComposerState(kLayerId,
+ fence(Fence::Status::Unsignaled),
+ layer_state_t::eCropChanged |
+ layer_state_t::
+ eBufferChanged),
+ });
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueueSameApplyTokenMultiState) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto mixedTransaction =
@@ -492,8 +475,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTransaction) {
@@ -501,7 +483,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto mixedTransaction =
@@ -514,8 +495,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) {
@@ -523,7 +503,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
@@ -540,8 +519,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest,
@@ -552,7 +530,6 @@
const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -579,43 +556,7 @@
});
setTransactionStates({unsignaledTransaction, signaledTransaction, signaledTransaction2},
- kExpectedTransactionsApplied, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
- const sp<IBinder> kApplyToken1 =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
- const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
- const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
- const auto kLayerId1 = 1;
- const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
- const auto kExpectedTransactionsPending = 1u;
-
- const auto signaledTransaction =
- createTransactionInfo(kApplyToken1,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto signaledTransaction2 =
- createTransactionInfo(kApplyToken2,
- {
- createComposerState(kLayerId1,
- fence(Fence::Status::Signaled),
- layer_state_t::eBufferChanged),
- });
- const auto unsignaledTransaction =
- createTransactionInfo(kApplyToken3,
- {
- createComposerState(kLayerId2,
- fence(Fence::Status::Unsignaled),
- layer_state_t::eBufferChanged),
- });
-
- setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
- kExpectedTransactionsApplied, kExpectedTransactionsPending);
+ kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
@@ -623,7 +564,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -640,7 +580,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+ setTransactionStates({unsignaledTransaction, signaledTransaction},
kExpectedTransactionsPending);
}
@@ -650,7 +590,6 @@
const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -668,14 +607,13 @@
layer_state_t::eBufferChanged),
});
setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsApplied, kExpectedTransactionsPending);
+ kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAutoSingleLayerTest, DontLatchUnsignaledWhenEarlyOffset) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -689,8 +627,7 @@
// Get VsyncModulator out of the default config
static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
class LatchUnsignaledDisabledTest : public LatchUnsignaledTest {
@@ -705,22 +642,19 @@
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
createTransactionInfo(kApplyToken,
{createComposerState(kLayerId, fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged)});
- setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueue) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -730,15 +664,13 @@
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueSameLayerId) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -751,8 +683,7 @@
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) {
@@ -760,7 +691,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 0u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -773,8 +703,7 @@
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
@@ -782,7 +711,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
@@ -799,8 +727,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) {
@@ -809,7 +736,6 @@
const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 1u;
const auto unsignaledTransaction =
@@ -826,7 +752,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+ setTransactionStates({unsignaledTransaction, signaledTransaction},
kExpectedTransactionsPending);
}
@@ -835,7 +761,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 1u;
const auto signaledTransaction =
@@ -852,7 +777,7 @@
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+ setTransactionStates({signaledTransaction, unsignaledTransaction},
kExpectedTransactionsPending);
}
@@ -861,8 +786,7 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 0u;
- const auto kExpectedTransactionsPending = 1u;
+ const auto kExpectedTransactionsPending = 2u;
const auto unsignaledTransaction =
createTransactionInfo(kApplyToken,
@@ -879,7 +803,7 @@
layer_state_t::eBufferChanged),
});
setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsApplied, kExpectedTransactionsPending);
+ kExpectedTransactionsPending);
}
class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
@@ -894,37 +818,32 @@
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
createTransactionInfo(kApplyToken,
{createComposerState(kLayerId, fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged)});
- setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto unsignaledTransaction =
createTransactionInfo(kApplyToken,
{createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged)});
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto mixedTransaction =
@@ -933,8 +852,7 @@
layer_state_t::eBufferChanged),
createComposerState(kLayerId, fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged)});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
@@ -942,7 +860,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto mixedTransaction =
@@ -951,8 +868,7 @@
layer_state_t::eBufferChanged),
createComposerState(kLayerId2, fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged)});
- setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
@@ -960,7 +876,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
@@ -977,8 +892,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
@@ -987,7 +901,6 @@
const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 0u;
const auto signaledTransaction =
@@ -1004,7 +917,7 @@
fence(Fence::Status::Unsignaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+ setTransactionStates({signaledTransaction, unsignaledTransaction},
kExpectedTransactionsPending);
}
@@ -1013,7 +926,6 @@
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 0u;
const auto unsignaledTransaction =
@@ -1030,7 +942,7 @@
fence(Fence::Status::Signaled),
layer_state_t::eBufferChanged),
});
- setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+ setTransactionStates({unsignaledTransaction, signaledTransaction},
kExpectedTransactionsPending);
}
@@ -1040,7 +952,6 @@
const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
const auto kLayerId1 = 1;
const auto kLayerId2 = 2;
- const auto kExpectedTransactionsApplied = 2u;
const auto kExpectedTransactionsPending = 0u;
const auto unsignaledTransaction =
@@ -1058,14 +969,13 @@
layer_state_t::eBufferChanged),
});
setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
- kExpectedTransactionsApplied, kExpectedTransactionsPending);
+ kExpectedTransactionsPending);
}
TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
const auto kLayerId = 1;
- const auto kExpectedTransactionsApplied = 1u;
const auto kExpectedTransactionsPending = 0u;
const auto unsignaledTransaction =
@@ -1079,8 +989,19 @@
// Get VsyncModulator out of the default config
static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
- setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
- kExpectedTransactionsPending);
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
+TEST(TransactionHandlerTest, QueueTransaction) {
+ TransactionHandler handler;
+ TransactionState transaction;
+ transaction.applyToken = sp<BBinder>::make();
+ transaction.id = 42;
+ handler.queueTransaction(std::move(transaction));
+ std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+
+ EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
+ EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index 1f011be..b6427c0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -46,13 +46,14 @@
size_t layerCount = 2;
t1.states.reserve(layerCount);
for (uint32_t i = 0; i < layerCount; i++) {
- ComposerState s;
+ ResolvedComposerState s;
if (i == 1) {
layer.parentSurfaceControlForChild =
- sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42);
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), layerHandle, 42,
+ "#42");
}
s.state = layer;
- t1.states.add(s);
+ t1.states.emplace_back(s);
}
size_t displayCount = 2;
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 2dbcfbd..482c3a8 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -112,16 +112,16 @@
{
TransactionState transaction;
transaction.id = 50;
- ComposerState layerState;
+ ResolvedComposerState layerState;
layerState.state.surface = fakeLayerHandle;
layerState.state.what = layer_state_t::eLayerChanged;
layerState.state.z = 42;
- transaction.states.add(layerState);
- ComposerState childState;
+ transaction.states.emplace_back(layerState);
+ ResolvedComposerState childState;
childState.state.surface = fakeChildLayerHandle;
childState.state.what = layer_state_t::eLayerChanged;
childState.state.z = 43;
- transaction.states.add(childState);
+ transaction.states.emplace_back(childState);
mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
@@ -138,12 +138,12 @@
{
TransactionState transaction;
transaction.id = 51;
- ComposerState layerState;
+ ResolvedComposerState layerState;
layerState.state.surface = fakeLayerHandle;
layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
layerState.state.z = 41;
layerState.state.x = 22;
- transaction.states.add(layerState);
+ transaction.states.emplace_back(layerState);
mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
@@ -247,16 +247,16 @@
{
TransactionState transaction;
transaction.id = 50;
- ComposerState layerState;
+ ResolvedComposerState layerState;
layerState.state.surface = fakeLayerHandle;
layerState.state.what = layer_state_t::eLayerChanged;
layerState.state.z = 42;
- transaction.states.add(layerState);
- ComposerState mirrorState;
+ transaction.states.emplace_back(layerState);
+ ResolvedComposerState mirrorState;
mirrorState.state.surface = fakeMirrorLayerHandle;
mirrorState.state.what = layer_state_t::eLayerChanged;
mirrorState.state.z = 43;
- transaction.states.add(mirrorState);
+ transaction.states.emplace_back(mirrorState);
mTracing.addQueuedTransaction(transaction);
std::vector<TransactionState> transactions;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 2da266b..47c2dee 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,6 +54,7 @@
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ void setDivisor(unsigned) final {}
void dump(std::string&) const final {}
private:
@@ -91,6 +92,7 @@
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ void setDivisor(unsigned) final {}
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f660753..2b86e94 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -55,6 +55,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(void, setDivisor, (unsigned), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 74d2b7d..3095e8a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -532,6 +532,26 @@
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
}
+TEST_F(VSyncPredictorTest, setDivisorIsRespected) {
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+
+ tracker.setDivisor(3);
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index a35ff96..8bd689a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,6 +50,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(void, setDivisor, (unsigned), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
index c2c3d77..5654691 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -36,7 +36,7 @@
MockAidlPowerHalWrapper();
~MockAidlPowerHalWrapper() override;
MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
- MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(bool, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
MOCK_METHOD(void, restartPowerHintSession, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index aa8b521..836e3a4 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -32,7 +32,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::composer::V2_1::Config;
@@ -56,8 +55,8 @@
std::vector<aidl::android::hardware::graphics::composer3::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&));
- MOCK_METHOD0(resetCommands, void());
- MOCK_METHOD0(executeCommands, Error());
+ MOCK_METHOD1(resetCommands, void(Display));
+ MOCK_METHOD1(executeCommands, Error(Display));
MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
@@ -164,6 +163,10 @@
MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds));
MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*));
MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*));
+ MOCK_METHOD1(getOverlaySupport,
+ Error(aidl::android::hardware::graphics::composer3::OverlayProperties*));
+ MOCK_METHOD1(onHotplugConnect, void(Display));
+ MOCK_METHOD1(onHotplugDisconnect, void(Display));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index a83ecbc..c78b6bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -33,4 +33,9 @@
.build();
}
+inline DisplayModePtr createDisplayMode(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps refreshRate) {
+ return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
+}
+
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 07cd15d..40f59b8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -105,6 +105,9 @@
MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override));
MOCK_METHOD(hal::Error, getPhysicalDisplayOrientation, (Hwc2::AidlTransform *),
(const override));
+ MOCK_METHOD(hal::Error, getOverlaySupport,
+ (aidl::android::hardware::graphics::composer3::OverlayProperties *),
+ (const override));
};
class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 439f6f4..f4ded21 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -23,6 +23,7 @@
using android::binder::Status;
using android::hardware::power::IPowerHintSession;
+using android::hardware::power::SessionHint;
using namespace android::hardware::power;
@@ -40,6 +41,8 @@
MOCK_METHOD(std::string, getInterfaceHash, (), (override));
MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
+ MOCK_METHOD(Status, sendHint, (SessionHint), (override));
+ MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (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 fb1b394..7fc625c 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -32,7 +32,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
- MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
new file mode 100644
index 0000000..a71e82c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <android/gui/DisplayModeSpecs.h>
+
+namespace android::mock {
+
+inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching,
+ float minFps, float maxFps) {
+ gui::DisplayModeSpecs specs;
+ specs.defaultMode = defaultMode;
+ specs.allowGroupSwitching = allowGroupSwitching;
+ specs.primaryRanges.physical.min = minFps;
+ specs.primaryRanges.physical.max = maxFps;
+ specs.primaryRanges.render = specs.primaryRanges.physical;
+ specs.appRequestRanges = specs.primaryRanges;
+ return specs;
+}
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ded6806..f8567bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,7 +34,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
+ MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
MOCK_METHOD2(onFrameRateOverridesChanged,
void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));
diff --git a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
similarity index 71%
copy from libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
copy to services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index 9fac3e8..ef9cd9b 100644
--- a/libs/gui/aidl/android/gui/MirrorSurfaceResult.aidl
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.gui;
+#pragma once
-/** @hide */
-parcelable MirrorSurfaceResult {
- IBinder handle;
- int layerId;
-}
+#include <scheduler/FrameRateMode.h>
+
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
+ EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 5267586..7d4b159 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -24,14 +24,14 @@
struct SchedulerCallback final : ISchedulerCallback {
MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
- MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override));
+ MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
void setVsyncEnabled(bool) override {}
- void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+ void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() override {}
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
deleted file mode 100644
index 0a0e7b5..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 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/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "mock/MockSurfaceInterceptor.h"
-
-namespace android::mock {
-
-// Explicit default instantiation is recommended.
-SurfaceInterceptor::SurfaceInterceptor() = default;
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-} // namespace android::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
deleted file mode 100644
index b085027..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 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 "SurfaceInterceptor.h"
-
-namespace android::mock {
-
-class SurfaceInterceptor : public android::SurfaceInterceptor {
-public:
- SurfaceInterceptor();
- ~SurfaceInterceptor() override;
-
- MOCK_METHOD2(enable,
- void(const SortedVector<sp<Layer>>&,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
- MOCK_METHOD0(disable, void());
- MOCK_METHOD0(isEnabled, bool());
- MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&));
- MOCK_METHOD1(binderDied, void(const wp<IBinder>&));
- MOCK_METHOD7(saveTransaction,
- void(const Vector<ComposerState>&,
- const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
- const Vector<DisplayState>&, uint32_t, int, int, uint64_t));
- MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
- MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
- MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
- MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
- MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
- MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
- MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 0dee800..86fbadc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
TimeStats();
~TimeStats() override;
- MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
+ MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*));
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 5b0c1f3..6893154 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,6 +34,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(void, setDivisor, (unsigned), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 224868c..f297da5 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -51,7 +51,11 @@
}
static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
- captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ // TODO(b/248317436): extend to cover all displays for multi-display devices
+ const auto display =
+ ids.empty() ? nullptr : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ captureScreen(sc, display);
}
static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
deleted file mode 100644
index 80e9a3c..0000000
--- a/services/vr/hardware_composer/Android.bp
+++ /dev/null
@@ -1,134 +0,0 @@
-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_shared {
- name: "libvr_hwc-hal",
-
- system_ext_specific: true,
-
- srcs: [
- "impl/vr_hwc.cpp",
- "impl/vr_composer_client.cpp",
- ],
-
- static_libs: [
- "libbroadcastring",
- "libdisplay",
- ],
-
- shared_libs: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- "android.hardware.graphics.composer@2.1-resources",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
- "libbase",
- "libbufferhubqueue",
- "libbinder",
- "libcutils",
- "libfmq",
- "libhardware",
- "libhidlbase",
- "liblog",
- "libsync",
- "libui",
- "libutils",
- "libpdx_default_transport",
- ],
-
- header_libs: [
- "android.hardware.graphics.composer@2.1-command-buffer",
- "android.hardware.graphics.composer@2.3-hal",
- ],
-
- export_header_lib_headers: [
- "android.hardware.graphics.composer@2.3-hal",
- ],
-
- export_static_lib_headers: [
- "libdisplay",
- ],
-
- export_shared_lib_headers: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- ],
-
- export_include_dirs: ["."],
-
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-Wall",
- "-Werror",
- "-Wno-error=unused-private-field",
- // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
- "-Wno-sign-compare",
- "-Wno-unused-parameter",
- ],
-
-}
-
-cc_library_static {
- name: "libvr_hwc-impl",
- srcs: [
- "vr_composer.cpp",
- ],
- static_libs: [
- "libvr_hwc-binder",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
- export_shared_lib_headers: [
- "libvr_hwc-hal",
- ],
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-Wall",
- "-Werror",
- ],
-}
-
-cc_test {
- name: "vr_hwc_test",
- gtest: true,
- srcs: ["tests/vr_composer_test.cpp"],
- static_libs: [
- "libgtest",
- "libvr_hwc-impl",
- // NOTE: This needs to be included after the *-impl lib otherwise the
- // symbols in the *-binder library get optimized out.
- "libvr_hwc-binder",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- // warnings in vr_composer_test.cpp to be fixed after merge of goog/master
- "-Wno-sign-compare",
- "-Wno-unused-parameter",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libui",
- "libutils",
- ],
-}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
deleted file mode 100644
index fa71ed7..0000000
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-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_static {
- name: "libvr_hwc-binder",
- srcs: [
- "android/dvr/IVrComposer.aidl",
- "android/dvr/IVrComposerCallback.aidl",
- "android/dvr/parcelable_composer_frame.cpp",
- "android/dvr/parcelable_composer_layer.cpp",
- "android/dvr/parcelable_unique_fd.cpp",
- ],
- aidl: {
- local_include_dirs: ["."],
- export_aidl_headers: true,
- },
- export_include_dirs: ["."],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- shared_libs: [
- "libbinder",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
deleted file mode 100644
index be1ec5b..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-package android.dvr;
-
-import android.dvr.IVrComposerCallback;
-
-/**
- * Service interface exposed by VR HWC exposed to system apps which allows one
- * system app to connect to get SurfaceFlinger's outputs (all displays). This
- * is active when SurfaceFlinger is in VR mode, where all 2D output is
- * redirected to VR HWC.
- *
- * @hide */
-interface IVrComposer
-{
- const String SERVICE_NAME = "vr_hwc";
-
- /**
- * Registers a callback used to receive frame notifications.
- */
- void registerObserver(in IVrComposerCallback callback);
-
- /**
- * Clears a previously registered frame notification callback.
- */
- void clearObserver();
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
deleted file mode 100644
index aa70de1..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-package android.dvr;
-
-import android.dvr.ParcelableComposerFrame;
-import android.dvr.ParcelableUniqueFd;
-
-/**
- * A system app will implement and register this callback with VRComposer
- * to receive the layers SurfaceFlinger presented when in VR mode.
- *
- * @hide */
-interface IVrComposerCallback {
- /**
- * Called by the VR HWC service when a new frame is ready to be presented.
- *
- * @param frame The new frame VR HWC wants to present.
- * @return A fence FD used to signal when the previous frame is no longer
- * used by the client. This may be an invalid fence (-1) if the client is not
- * using the previous frame, in which case the previous frame may be re-used
- * at any point in time.
- */
- ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame);
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
deleted file mode 100644
index 84abc19..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
deleted file mode 100644
index a200345..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
deleted file mode 100644
index eee9d13..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
deleted file mode 100644
index db7d5dc..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_frame.h"
-
-#include <binder/Parcel.h>
-
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerFrame::ParcelableComposerFrame() {}
-
-ParcelableComposerFrame::ParcelableComposerFrame(
- const ComposerView::Frame& frame)
- : frame_(frame) {}
-
-ParcelableComposerFrame::~ParcelableComposerFrame() {}
-
-status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const {
- status_t ret = parcel->writeUint64(frame_.display_id);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(frame_.display_width);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(frame_.display_height);
- if (ret != OK) return ret;
-
- ret = parcel->writeBool(frame_.removed);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config));
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode));
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode));
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled));
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(frame_.color_transform_hint);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < 16; i++) {
- ret = parcel->writeFloat(frame_.color_transform[i]);
- if (ret != OK) return ret;
- }
-
- std::vector<ParcelableComposerLayer> layers;
- for (size_t i = 0; i < frame_.layers.size(); ++i)
- layers.push_back(ParcelableComposerLayer(frame_.layers[i]));
-
- ret = parcel->writeParcelableVector(layers);
-
- return ret;
-}
-
-status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) {
- status_t ret = parcel->readUint64(&frame_.display_id);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&frame_.display_width);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&frame_.display_height);
- if (ret != OK) return ret;
-
- ret = parcel->readBool(&frame_.removed);
- if (ret != OK) return ret;
-
- uint32_t value;
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.active_config = static_cast<Config>(value);
-
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.color_mode = static_cast<ColorMode>(value);
-
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.power_mode = static_cast<IComposerClient::PowerMode>(value);
-
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value);
-
- ret = parcel->readInt32(&frame_.color_transform_hint);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < 16; i++) {
- ret = parcel->readFloat(&frame_.color_transform[i]);
- if (ret != OK) return ret;
- }
-
- std::vector<ParcelableComposerLayer> layers;
- ret = parcel->readParcelableVector(&layers);
- if (ret != OK) return ret;
-
- frame_.layers.clear();
- for (size_t i = 0; i < layers.size(); ++i)
- frame_.layers.push_back(layers[i].layer());
-
- return ret;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
deleted file mode 100644
index a82df7f..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerFrame : public Parcelable {
- public:
- ParcelableComposerFrame();
- explicit ParcelableComposerFrame(const ComposerView::Frame& frame);
- ~ParcelableComposerFrame() override;
-
- ComposerView::Frame frame() const { return frame_; }
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- ComposerView::Frame frame_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
deleted file mode 100644
index c3621eb..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-#include <binder/Parcel.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicBufferMapper.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerLayer::ParcelableComposerLayer() {}
-
-ParcelableComposerLayer::ParcelableComposerLayer(
- const ComposerView::ComposerLayer& layer) : layer_(layer) {}
-
-ParcelableComposerLayer::~ParcelableComposerLayer() {}
-
-status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const {
- status_t ret = parcel->writeUint64(layer_.id);
- if (ret != OK) return ret;
-
- ret = parcel->write(*layer_.buffer);
- if (ret != OK) return ret;
-
- ret = parcel->writeBool(layer_.fence->isValid());
- if (ret != OK) return ret;
-
- if (layer_.fence->isValid()) {
- ret = parcel->writeFileDescriptor(layer_.fence->dup(), true);
- if (ret != OK) return ret;
- }
-
- ret = parcel->writeInt32(layer_.display_frame.left);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.display_frame.top);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.display_frame.right);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.display_frame.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.left);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.top);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.right);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode));
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.alpha);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(layer_.type);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(layer_.app_id);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(layer_.z_order);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.cursor_x);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.cursor_y);
- if (ret != OK) return ret;
-
- uint32_t color = layer_.color.r |
- (static_cast<uint32_t>(layer_.color.g) << 8) |
- (static_cast<uint32_t>(layer_.color.b) << 16) |
- (static_cast<uint32_t>(layer_.color.a) << 24);
- ret = parcel->writeUint32(color);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.dataspace);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.transform);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size()));
- if (ret != OK) return ret;
-
- for (auto& rect: layer_.visible_regions) {
- ret = parcel->writeInt32(rect.left);
- ret = parcel->writeInt32(rect.top);
- ret = parcel->writeInt32(rect.right);
- ret = parcel->writeInt32(rect.bottom);
- if (ret != OK) return ret;
- }
-
- ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size()));
- if (ret != OK) return ret;
-
- for (auto& rect: layer_.damaged_regions) {
- ret = parcel->writeInt32(rect.left);
- ret = parcel->writeInt32(rect.top);
- ret = parcel->writeInt32(rect.right);
- ret = parcel->writeInt32(rect.bottom);
- if (ret != OK) return ret;
- }
-
- return OK;
-}
-
-status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) {
- status_t ret = parcel->readUint64(&layer_.id);
- if (ret != OK) return ret;
-
- layer_.buffer = new GraphicBuffer();
- ret = parcel->read(*layer_.buffer);
- if (ret != OK) {
- layer_.buffer.clear();
- return ret;
- }
-
- bool has_fence = 0;
- ret = parcel->readBool(&has_fence);
- if (ret != OK) return ret;
-
- if (has_fence)
- layer_.fence = new Fence(dup(parcel->readFileDescriptor()));
- else
- layer_.fence = new Fence();
-
- ret = parcel->readInt32(&layer_.display_frame.left);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.display_frame.top);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.display_frame.right);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.display_frame.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.left);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.top);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.right);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode));
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.alpha);
- if (ret != OK) return ret;
-
- ret = parcel->readUint32(&layer_.type);
- if (ret != OK) return ret;
-
- ret = parcel->readUint32(&layer_.app_id);
- if (ret != OK) return ret;
-
- ret = parcel->readUint32(&layer_.z_order);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.cursor_x);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.cursor_y);
- if (ret != OK) return ret;
-
- uint32_t color;
- ret = parcel->readUint32(&color);
- if (ret != OK) return ret;
- layer_.color.r = color & 0xFF;
- layer_.color.g = (color >> 8) & 0xFF;
- layer_.color.b = (color >> 16) & 0xFF;
- layer_.color.a = (color >> 24) & 0xFF;
-
- ret = parcel->readInt32(&layer_.dataspace);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.transform);
- if (ret != OK) return ret;
-
- uint32_t size;
- ret = parcel->readUint32(&size);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < size; i++) {
- hwc_rect_t rect;
- ret = parcel->readInt32(&rect.left);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.top);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.right);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.bottom);
- if (ret != OK) return ret;
-
- layer_.visible_regions.push_back(rect);
- }
-
- ret = parcel->readUint32(&size);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < size; i++) {
- hwc_rect_t rect;
- ret = parcel->readInt32(&rect.left);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.top);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.right);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.bottom);
- if (ret != OK) return ret;
-
- layer_.damaged_regions.push_back(rect);
- }
-
- return OK;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
deleted file mode 100644
index 6d2ac09..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerLayer : public Parcelable {
- public:
- ParcelableComposerLayer();
- explicit ParcelableComposerLayer(const ComposerView::ComposerLayer& layer);
- ~ParcelableComposerLayer() override;
-
- ComposerView::ComposerLayer layer() const { return layer_; }
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- ComposerView::ComposerLayer layer_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
deleted file mode 100644
index 9486f3c..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "android/dvr/parcelable_unique_fd.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableUniqueFd::ParcelableUniqueFd() {}
-
-ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence)
- : fence_(dup(fence.get())) {}
-
-ParcelableUniqueFd::~ParcelableUniqueFd() {}
-
-status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const {
- status_t ret = parcel->writeBool(fence_.get() >= 0);
- if (ret != OK) return ret;
-
- if (fence_.get() >= 0)
- ret = parcel->writeUniqueFileDescriptor(fence_);
-
- return ret;
-}
-
-status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) {
- bool has_fence = 0;
- status_t ret = parcel->readBool(&has_fence);
- if (ret != OK) return ret;
-
- if (has_fence)
- ret = parcel->readUniqueFileDescriptor(&fence_);
-
- return ret;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
deleted file mode 100644
index c4216f6..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-
-#include <android-base/unique_fd.h>
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace dvr {
-
-// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the
-// case where the FD is invalid (-1), unlike FileDescriptor which expects a
-// valid FD.
-class ParcelableUniqueFd : public Parcelable {
- public:
- ParcelableUniqueFd();
- explicit ParcelableUniqueFd(const base::unique_fd& fence);
- ~ParcelableUniqueFd() override;
-
- void set_fence(const base::unique_fd& fence) {
- fence_.reset(dup(fence.get()));
- }
- base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); }
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- base::unique_fd fence_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
deleted file mode 100644
index dd1603d..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2016 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/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <hardware/gralloc.h>
-#include <hardware/gralloc1.h>
-#include <log/log.h>
-
-#include <memory>
-
-#include "impl/vr_hwc.h"
-#include "impl/vr_composer_client.h"
-
-namespace android {
-namespace dvr {
-
-using android::frameworks::vr::composer::V2_0::IVrComposerClient;
-
-VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
- : ComposerClient(&hal), mVrHal(hal) {
- if (!init()) {
- LOG_ALWAYS_FATAL("failed to initialize VrComposerClient");
- }
-}
-
-VrComposerClient::~VrComposerClient() {}
-
-std::unique_ptr<ComposerCommandEngine>
-VrComposerClient::createCommandEngine() {
- return std::make_unique<VrCommandEngine>(*this);
-}
-
-VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
- : ComposerCommandEngine(client.mHal, client.mResources.get()),
- mVrHal(client.mVrHal) {}
-
-VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
-
-bool VrComposerClient::VrCommandEngine::executeCommand(
- hardware::graphics::composer::V2_1::IComposerClient::Command command,
- uint16_t length) {
- IVrComposerClient::VrCommand vrCommand =
- static_cast<IVrComposerClient::VrCommand>(command);
- switch (vrCommand) {
- case IVrComposerClient::VrCommand::SET_LAYER_INFO:
- return executeSetLayerInfo(length);
- case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA:
- return executeSetClientTargetMetadata(length);
- case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA:
- return executeSetLayerBufferMetadata(length);
- default:
- return ComposerCommandEngine::executeCommand(command, length);
- }
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerInfo(uint16_t length) {
- if (length != 2) {
- return false;
- }
-
- auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
- if (err != Error::NONE) {
- mWriter->setError(getCommandLoc(), err);
- }
-
- return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetClientTargetMetadata(
- uint16_t length) {
- if (length != 7)
- return false;
-
- auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
- if (err != Error::NONE)
- mWriter->setError(getCommandLoc(), err);
-
- return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata(
- uint16_t length) {
- if (length != 7)
- return false;
-
- auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
- readBufferMetadata());
- if (err != Error::NONE)
- mWriter->setError(getCommandLoc(), err);
-
- return true;
-}
-
-IVrComposerClient::BufferMetadata
-VrComposerClient::VrCommandEngine::readBufferMetadata() {
- IVrComposerClient::BufferMetadata metadata = {
- .width = read(),
- .height = read(),
- .stride = read(),
- .layerCount = read(),
- .format =
- static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
- readSigned()),
- .usage = read64(),
- };
- return metadata;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
deleted file mode 100644
index 1b2b5f4..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.1/ComposerCommandEngine.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-
-namespace android {
-namespace dvr {
-
-class VrHwc;
-
-using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine;
-using hardware::graphics::composer::V2_3::hal::ComposerHal;
-using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl;
-
-using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>;
-
-class VrComposerClient : public ComposerClient {
- public:
- explicit VrComposerClient(android::dvr::VrHwc& hal);
- virtual ~VrComposerClient();
-
- private:
- class VrCommandEngine : public ComposerCommandEngine {
- public:
- explicit VrCommandEngine(VrComposerClient& client);
- ~VrCommandEngine() override;
-
- bool executeCommand(
- hardware::graphics::composer::V2_1::IComposerClient::Command command,
- uint16_t length) override;
-
- private:
- bool executeSetLayerInfo(uint16_t length);
- bool executeSetClientTargetMetadata(uint16_t length);
- bool executeSetLayerBufferMetadata(uint16_t length);
-
- IVrComposerClient::BufferMetadata readBufferMetadata();
-
- android::dvr::VrHwc& mVrHal;
-
- VrCommandEngine(const VrCommandEngine&) = delete;
- void operator=(const VrCommandEngine&) = delete;
- };
-
- VrComposerClient(const VrComposerClient&) = delete;
- void operator=(const VrComposerClient&) = delete;
-
- std::unique_ptr<ComposerCommandEngine> createCommandEngine() override;
- dvr::VrHwc& mVrHal;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
deleted file mode 100644
index e530b16..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ /dev/null
@@ -1,1178 +0,0 @@
-/*
- * Copyright 2016 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 "impl/vr_hwc.h"
-
-#include "android-base/stringprintf.h"
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <private/dvr/display_client.h>
-#include <ui/Fence.h>
-#include <utils/Trace.h>
-
-#include <mutex>
-
-#include "vr_composer_client.h"
-
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::base::StringPrintf;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-
-namespace types = android::hardware::graphics::common;
-
-namespace android {
-namespace dvr {
-namespace {
-
-const Display kDefaultDisplayId = 1;
-const Config kDefaultConfigId = 1;
-
-sp<GraphicBuffer> CreateGraphicBuffer(
- const native_handle_t* handle,
- const IVrComposerClient::BufferMetadata& metadata) {
- sp<GraphicBuffer> buffer = new GraphicBuffer(
- handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height,
- static_cast<int32_t>(metadata.format), metadata.layerCount,
- metadata.usage, metadata.stride);
- if (buffer->initCheck() != OK) {
- ALOGE("Failed to create graphic buffer");
- return nullptr;
- }
-
- return buffer;
-}
-
-void GetPrimaryDisplaySize(int32_t* width, int32_t* height) {
- *width = 1080;
- *height = 1920;
-
- int error = 0;
- auto display_client = display::DisplayClient::Create(&error);
- if (!display_client) {
- ALOGE("Could not connect to display service : %s(%d)", strerror(error),
- error);
- return;
- }
-
- auto status = display_client->GetDisplayMetrics();
- if (!status) {
- ALOGE("Could not get display metrics from display service : %s(%d)",
- status.GetErrorMessage().c_str(), status.error());
- return;
- }
-
- *width = status.get().display_width;
- *height = status.get().display_height;
-}
-
-} // namespace
-
-HwcDisplay::HwcDisplay(int32_t width, int32_t height)
- : width_(width), height_(height) {}
-
-HwcDisplay::~HwcDisplay() {}
-
-bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
- base::unique_fd fence) {
- if (handle)
- buffer_ = CreateGraphicBuffer(handle, buffer_metadata_);
-
- fence_ = new Fence(fence.release());
- return true;
-}
-
-void HwcDisplay::SetClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata) {
- buffer_metadata_ = metadata;
-}
-
-HwcLayer* HwcDisplay::CreateLayer() {
- uint64_t layer_id = layer_ids_++;
- layers_.push_back(HwcLayer(layer_id));
- return &layers_.back();
-}
-
-HwcLayer* HwcDisplay::GetLayer(Layer id) {
- for (size_t i = 0; i < layers_.size(); ++i)
- if (layers_[i].info.id == id)
- return &layers_[i];
-
- return nullptr;
-}
-
-bool HwcDisplay::DestroyLayer(Layer id) {
- for (auto it = layers_.begin(); it != layers_.end(); ++it) {
- if (it->info.id == id) {
- layers_.erase(it);
- return true;
- }
- }
-
- return false;
-}
-
-void HwcDisplay::GetChangedCompositionTypes(
- std::vector<Layer>* layer_ids,
- std::vector<IComposerClient::Composition>* types) {
- std::sort(layers_.begin(), layers_.end(),
- [](const auto& lhs, const auto& rhs) {
- return lhs.info.z_order < rhs.info.z_order;
- });
-
- const size_t no_layer = std::numeric_limits<size_t>::max();
- size_t first_client_layer = no_layer, last_client_layer = no_layer;
- for (size_t i = 0; i < layers_.size(); ++i) {
- switch (layers_[i].composition_type) {
- case IComposerClient::Composition::SOLID_COLOR:
- case IComposerClient::Composition::CURSOR:
- case IComposerClient::Composition::SIDEBAND:
- if (first_client_layer == no_layer)
- first_client_layer = i;
-
- last_client_layer = i;
- break;
- default:
- break;
- }
- }
-
- for (size_t i = 0; i < layers_.size(); ++i) {
- if (i >= first_client_layer && i <= last_client_layer) {
- if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) {
- layer_ids->push_back(layers_[i].info.id);
- types->push_back(IComposerClient::Composition::CLIENT);
- layers_[i].composition_type = IComposerClient::Composition::CLIENT;
- }
-
- continue;
- }
-
- if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
- layer_ids->push_back(layers_[i].info.id);
- types->push_back(IComposerClient::Composition::DEVICE);
- layers_[i].composition_type = IComposerClient::Composition::DEVICE;
- }
- }
-}
-
-Error HwcDisplay::GetFrame(
- std::vector<ComposerView::ComposerLayer>* out_frames) {
- bool queued_client_target = false;
- std::vector<ComposerView::ComposerLayer> frame;
- for (const auto& layer : layers_) {
- if (layer.composition_type == IComposerClient::Composition::CLIENT) {
- if (queued_client_target)
- continue;
-
- if (!buffer_.get()) {
- ALOGE("Client composition requested but no client target buffer");
- return Error::BAD_LAYER;
- }
-
- ComposerView::ComposerLayer client_target_layer = {
- .buffer = buffer_,
- .fence = fence_.get() ? fence_ : new Fence(-1),
- .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
- static_cast<int32_t>(buffer_->getHeight())},
- .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
- static_cast<float>(buffer_->getHeight())},
- .blend_mode = IComposerClient::BlendMode::NONE,
- };
-
- frame.push_back(client_target_layer);
- queued_client_target = true;
- } else {
- if (!layer.info.buffer.get() || !layer.info.fence.get()) {
- ALOGV("Layer requested without valid buffer");
- continue;
- }
-
- frame.push_back(layer.info);
- }
- }
-
- out_frames->swap(frame);
- return Error::NONE;
-}
-
-std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() {
- std::vector<Layer> last_frame_layers;
- last_frame_layers.swap(last_frame_layers_ids_);
-
- for (const auto& layer : layers_)
- last_frame_layers_ids_.push_back(layer.info.id);
-
- return last_frame_layers;
-}
-
-void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) {
- color_transform_hint_ = hint;
- if (matrix)
- memcpy(color_transform_, matrix, sizeof(color_transform_));
-}
-
-void HwcDisplay::dumpDebugInfo(std::string* result) const {
- if (!result) {
- return;
- }
- *result += StringPrintf("HwcDisplay: width: %d, height: %d, layers size: %zu, colormode: %d\
- , config: %d\n", width_, height_, layers_.size(), color_mode_, active_config_);
- *result += StringPrintf("HwcDisplay buffer metadata: width: %d, height: %d, stride: %d,\
- layerCount: %d, pixelFormat: %d\n", buffer_metadata_.width, buffer_metadata_.height,
- buffer_metadata_.stride, buffer_metadata_.layerCount, buffer_metadata_.format);
- for (const auto& layer : layers_) {
- layer.dumpDebugInfo(result);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// VrHwcClient
-
-VrHwc::VrHwc() {
- vsync_callback_ = new VsyncCallback;
-}
-
-VrHwc::~VrHwc() {
- vsync_callback_->SetEventCallback(nullptr);
-}
-
-bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
-
-void VrHwc::registerEventCallback(EventCallback* callback) {
- std::unique_lock<std::mutex> lock(mutex_);
- event_callback_ = callback;
- int32_t width, height;
- GetPrimaryDisplaySize(&width, &height);
- // Create the primary display late to avoid initialization issues between
- // VR HWC and SurfaceFlinger.
- displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-
- // Surface flinger will make calls back into vr_hwc when it receives the
- // onHotplug() call, so it's important to release mutex_ here.
- lock.unlock();
- event_callback_->onHotplug(kDefaultDisplayId,
- hardware::graphics::composer::V2_1::
- IComposerCallback::Connection::CONNECTED);
- lock.lock();
- UpdateVsyncCallbackEnabledLocked();
-}
-
-void VrHwc::unregisterEventCallback() {
- std::lock_guard<std::mutex> guard(mutex_);
- event_callback_ = nullptr;
- UpdateVsyncCallbackEnabledLocked();
-}
-
-uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
-
-Error VrHwc::destroyVirtualDisplay(Display display) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (display == kDefaultDisplayId || displays_.erase(display) == 0)
- return Error::BAD_DISPLAY;
- ComposerView::Frame frame;
- frame.display_id = display;
- frame.removed = true;
- if (observer_)
- observer_->OnNewFrame(frame);
- return Error::NONE;
-}
-
-Error VrHwc::createLayer(Display display, Layer* outLayer) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* layer = display_ptr->CreateLayer();
- *outLayer = layer->info.id;
- return Error::NONE;
-}
-
-Error VrHwc::destroyLayer(Display display, Layer layer) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr) {
- return Error::BAD_DISPLAY;
- }
-
- return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
-}
-
-Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- *outConfig = kDefaultConfigId;
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayAttribute(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr) {
- return Error::BAD_DISPLAY;
- }
- if (config != kDefaultConfigId) {
- return Error::BAD_CONFIG;
- }
-
- switch (attribute) {
- case IComposerClient::Attribute::WIDTH:
- *outValue = display_ptr->width();
- break;
- case IComposerClient::Attribute::HEIGHT:
- *outValue = display_ptr->height();
- break;
- case IComposerClient::Attribute::VSYNC_PERIOD:
- {
- int error = 0;
- auto display_client = display::DisplayClient::Create(&error);
- if (!display_client) {
- ALOGE("Could not connect to display service : %s(%d)",
- strerror(error), error);
- // Return a default value of 30 fps
- *outValue = 1000 * 1000 * 1000 / 30;
- } else {
- auto metrics = display_client->GetDisplayMetrics();
- *outValue = metrics.get().vsync_period_ns;
- }
- }
- break;
- case IComposerClient::Attribute::DPI_X:
- case IComposerClient::Attribute::DPI_Y:
- {
- constexpr int32_t kDefaultDPI = 300;
- int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI);
- if (dpi <= 0) {
- dpi = kDefaultDPI;
- }
- *outValue = 1000 * dpi;
- }
- break;
- default:
- return Error::BAD_PARAMETER;
- }
-
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- std::vector<Config> configs(1, kDefaultConfigId);
- *outConfigs = hidl_vec<Config>(configs);
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
- *outName = hidl_string();
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayType(Display display,
- IComposerClient::DisplayType* outType) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr) {
- *outType = IComposerClient::DisplayType::INVALID;
- return Error::BAD_DISPLAY;
- }
-
- if (display == kDefaultDisplayId)
- *outType = IComposerClient::DisplayType::PHYSICAL;
- else
- *outType = IComposerClient::DisplayType::VIRTUAL;
-
- return Error::NONE;
-}
-
-Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
- *outSupport = false;
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- return Error::NONE;
-}
-
-Error VrHwc::setActiveConfig(Display display, Config config) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
- if (config != kDefaultConfigId)
- return Error::BAD_CONFIG;
-
- display_ptr->set_active_config(config);
- return Error::NONE;
-}
-
-Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (enabled != IComposerClient::Vsync::ENABLE &&
- enabled != IComposerClient::Vsync::DISABLE) {
- return Error::BAD_PARAMETER;
- }
-
- Error set_vsync_result = Error::NONE;
- if (display == kDefaultDisplayId) {
- sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
- defaultServiceManager()->getService(
- String16(IVsyncService::GetServiceName())));
- if (vsync_service == nullptr) {
- ALOGE("Failed to get vsync service");
- return Error::NO_RESOURCES;
- }
-
- if (enabled == IComposerClient::Vsync::ENABLE) {
- ALOGI("Enable vsync");
- display_ptr->set_vsync_enabled(true);
- status_t result = vsync_service->registerCallback(vsync_callback_);
- if (result != OK) {
- ALOGE("%s service registerCallback() failed: %s (%d)",
- IVsyncService::GetServiceName(), strerror(-result), result);
- set_vsync_result = Error::NO_RESOURCES;
- }
- } else if (enabled == IComposerClient::Vsync::DISABLE) {
- ALOGI("Disable vsync");
- display_ptr->set_vsync_enabled(false);
- status_t result = vsync_service->unregisterCallback(vsync_callback_);
- if (result != OK) {
- ALOGE("%s service unregisterCallback() failed: %s (%d)",
- IVsyncService::GetServiceName(), strerror(-result), result);
- set_vsync_result = Error::NO_RESOURCES;
- }
- }
-
- UpdateVsyncCallbackEnabledLocked();
- }
-
- return set_vsync_result;
-}
-
-Error VrHwc::setColorTransform(Display display, const float* matrix,
- int32_t hint) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- display_ptr->SetColorTransform(matrix, hint);
- return Error::NONE;
-}
-
-Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
- int32_t acquireFence, int32_t /* dataspace */,
- const std::vector<hwc_rect_t>& /* damage */) {
- base::unique_fd fence(acquireFence);
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (target == nullptr)
- return Error::NONE;
-
- if (!display_ptr->SetClientTarget(target, std::move(fence)))
- return Error::BAD_PARAMETER;
-
- return Error::NONE;
-}
-
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
- int32_t releaseFence) {
- base::unique_fd fence(releaseFence);
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- // TODO(dnicoara): Is it necessary to do anything here?
- return Error::NONE;
-}
-
-Error VrHwc::validateDisplay(
- Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* /* outDisplayRequestMask */,
- std::vector<Layer>* /* outRequestedLayers */,
- std::vector<uint32_t>* /* outRequestMasks */) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- display_ptr->GetChangedCompositionTypes(outChangedLayers,
- outCompositionTypes);
- return Error::NONE;
-}
-
-Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
-
-Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
- std::vector<Layer>* outLayers,
- std::vector<int32_t>* outReleaseFences) {
- *outPresentFence = -1;
- outLayers->clear();
- outReleaseFences->clear();
-
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
-
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- ComposerView::Frame frame;
- std::vector<Layer> last_frame_layers;
- Error status = display_ptr->GetFrame(&frame.layers);
- frame.display_id = display;
- frame.display_width = display_ptr->width();
- frame.display_height = display_ptr->height();
- frame.active_config = display_ptr->active_config();
- frame.power_mode = display_ptr->power_mode();
- frame.vsync_enabled = display_ptr->vsync_enabled() ?
- IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
- frame.color_transform_hint = display_ptr->color_transform_hint();
- frame.color_mode = display_ptr->color_mode();
- memcpy(frame.color_transform, display_ptr->color_transform(),
- sizeof(frame.color_transform));
- if (status != Error::NONE)
- return status;
-
- last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
-
- base::unique_fd fence;
- if (observer_)
- fence = observer_->OnNewFrame(frame);
-
- if (fence.get() < 0)
- return Error::NONE;
-
- *outPresentFence = dup(fence.get());
- outLayers->swap(last_frame_layers);
- for (size_t i = 0; i < outLayers->size(); ++i)
- outReleaseFences->push_back(dup(fence.get()));
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
- int32_t y) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.cursor_x = x;
- hwc_layer->info.cursor_y = y;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerBuffer(Display display, Layer layer,
- buffer_handle_t buffer, int32_t acquireFence) {
- base::unique_fd fence(acquireFence);
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.buffer = CreateGraphicBuffer(
- buffer, hwc_layer->buffer_metadata);
- hwc_layer->info.fence = new Fence(fence.release());
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<hwc_rect_t>& damage) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.damaged_regions = damage;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.blend_mode =
- static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerColor(Display display, Layer layer,
- IComposerClient::Color color) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.color = color;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerCompositionType(Display display, Layer layer,
- int32_t type) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerDataspace(Display display, Layer layer,
- int32_t dataspace) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.dataspace = dataspace;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
- const hwc_rect_t& frame) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.display_frame =
- {frame.left, frame.top, frame.right, frame.bottom};
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.alpha = alpha;
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
- buffer_handle_t /* stream */) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
- const hwc_frect_t& crop) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerTransform(Display display, Layer layer,
- int32_t transform) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.transform = transform;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<hwc_rect_t>& visible) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.visible_regions = visible;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.z_order = z;
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
- uint32_t appId) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.type = type;
- hwc_layer->info.app_id = appId;
-
- return Error::NONE;
-}
-
-Error VrHwc::setClientTargetMetadata(
- Display display, const IVrComposerClient::BufferMetadata& metadata) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- display_ptr->SetClientTargetMetadata(metadata);
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerBufferMetadata(
- Display display, Layer layer,
- const IVrComposerClient::BufferMetadata& metadata) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->buffer_metadata = metadata;
-
- return Error::NONE;
-}
-
-Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) {
- hidl_cb(hidl_vec<Capability>());
- return Void();
-}
-
-Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
- std::string result;
-
- {
- std::lock_guard<std::mutex> guard(mutex_);
- result = "\nVrHwc states:\n";
- for (const auto& pair : displays_) {
- result += StringPrintf("Display id: %lu\n", (unsigned long)pair.first);
- pair.second->dumpDebugInfo(&result);
- }
- result += "\n";
- }
-
- hidl_cb(hidl_string(result));
- return Void();
-}
-
-Return<void> VrHwc::createClient(createClient_cb hidl_cb) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- Error status = Error::NONE;
- sp<VrComposerClient> client;
- if (!client_.promote().get()) {
- client = new VrComposerClient(*this);
- } else {
- ALOGE("Already have a client");
- status = Error::NO_RESOURCES;
- }
-
- client_ = client;
- hidl_cb(status, client);
- return Void();
-}
-
-Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- Error status = Error::NONE;
- sp<VrComposerClient> client;
- if (!client_.promote().get()) {
- client = new VrComposerClient(*this);
- } else {
- ALOGE("Already have a client");
- status = Error::NO_RESOURCES;
- }
-
- client_ = client;
- hidl_cb(status, client);
- return Void();
-}
-
-void VrHwc::ForceDisplaysRefresh() {
- std::lock_guard<std::mutex> guard(mutex_);
- if (event_callback_ != nullptr) {
- for (const auto& pair : displays_)
- event_callback_->onRefresh(pair.first);
- }
-}
-
-void VrHwc::RegisterObserver(Observer* observer) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (observer_)
- ALOGE("Overwriting observer");
- else
- observer_ = observer;
-}
-
-void VrHwc::UnregisterObserver(Observer* observer) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (observer != observer_)
- ALOGE("Trying to unregister unknown observer");
- else
- observer_ = nullptr;
-}
-
-HwcDisplay* VrHwc::FindDisplay(Display display) {
- auto iter = displays_.find(display);
- return iter == displays_.end() ? nullptr : iter->second.get();
-}
-
-void VrHwc::UpdateVsyncCallbackEnabledLocked() {
- auto primary_display = FindDisplay(kDefaultDisplayId);
- LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
- "Should have created the primary display by now");
- bool send_vsync =
- event_callback_ != nullptr && primary_display->vsync_enabled();
- vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
-}
-
-Return<void> VrHwc::debug(const hidl_handle& fd,
- const hidl_vec<hidl_string>& args) {
- std::string result;
-
- {
- std::lock_guard<std::mutex> guard(mutex_);
- for (const auto& pair : displays_) {
- result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first));
- pair.second->dumpDebugInfo(&result);
- }
- result += "\n";
- }
-
- FILE* out = fdopen(dup(fd->data[0]), "w");
- fprintf(out, "%s", result.c_str());
- fclose(out);
-
- return Void();
-}
-
-void HwcLayer::dumpDebugInfo(std::string* result) const {
- if (!result) {
- return;
- }
- *result += StringPrintf("Layer: composition_type: %d, type: %d, app_id: %d, z_order: %d,\
- cursor_x: %d, cursor_y: %d, color(rgba): (%d,%d,%d,%d), dataspace: %d, transform: %d,\
- display_frame(LTRB): (%d,%d,%d,%d), crop(LTRB): (%.1f,%.1f,%.1f,%.1f), blend_mode: %d\n",
- composition_type, info.type, info.app_id, info.z_order, info.cursor_x, info.cursor_y,
- info.color.r, info.color.g, info.color.b, info.color.a, info.dataspace, info.transform,
- info.display_frame.left, info.display_frame.top, info.display_frame.right,
- info.display_frame.bottom, info.crop.left, info.crop.top, info.crop.right,
- info.crop.bottom, info.blend_mode);
- *result += StringPrintf("Layer buffer metadata: width: %d, height: %d, stride: %d, layerCount: %d\
- , pixelFormat: %d\n", buffer_metadata.width, buffer_metadata.height, buffer_metadata.stride,
- buffer_metadata.layerCount, buffer_metadata.format);
-}
-
-status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
- ATRACE_NAME("vr_hwc onVsync");
- std::lock_guard<std::mutex> guard(mutex_);
- if (callback_ != nullptr)
- callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
- return OK;
-}
-
-void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
- std::lock_guard<std::mutex> guard(mutex_);
- callback_ = callback;
-}
-
-// composer::V2_2::ComposerHal
-Error VrHwc::setReadbackBuffer(Display display,
- const native_handle_t* bufferHandle,
- android::base::unique_fd fenceFd) {
- return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferFence(Display display,
- android::base::unique_fd* outFenceFd) {
- return Error::NONE;
-}
-
-Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
- types::V1_1::PixelFormat* format,
- Display* outDisplay) {
- *format = types::V1_1::PixelFormat::RGBA_8888;
- *outDisplay = display_count_;
- displays_[display_count_].reset(new HwcDisplay(width, height));
- display_count_++;
- return Error::NONE;
-}
-
-Error VrHwc::setPowerMode_2_2(Display display,
- IComposerClient::PowerMode mode) {
- bool dozeSupported = false;
-
- Error dozeSupportError = getDozeSupport(display, &dozeSupported);
-
- if (dozeSupportError != Error::NONE)
- return dozeSupportError;
-
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (mode < IComposerClient::PowerMode::OFF ||
- mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
- return Error::BAD_PARAMETER;
- }
-
- if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE ||
- mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
- return Error::UNSUPPORTED;
- }
-
- display_ptr->set_power_mode(mode);
- return Error::NONE;
-}
-
-Error VrHwc::setLayerFloatColor(Display display, Layer layer,
- IComposerClient::FloatColor color) {
- return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode,
- std::vector<RenderIntent>* outIntents) {
- return Error::NONE;
-}
-
-std::array<float, 16> VrHwc::getDataspaceSaturationMatrix(
- types::V1_1::Dataspace dataspace) {
- return {};
-}
-
-// composer::V2_3::ComposerHal
-Error VrHwc::getHdrCapabilities_2_3(Display /*display*/,
- hidl_vec<Hdr>* /*outTypes*/,
- float* outMaxLuminance,
- float* outMaxAverageLuminance,
- float* outMinLuminance) {
- *outMaxLuminance = 0;
- *outMaxAverageLuminance = 0;
- *outMinLuminance = 0;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadata_2_3(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadata>& metadata) {
- return Error::NONE;
-}
-
-Error VrHwc::getPerFrameMetadataKeys_2_3(
- Display display,
- std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
- return Error::NONE;
-}
-
-Error VrHwc::setColorMode_2_3(Display display, ColorMode mode,
- RenderIntent intent) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
- return Error::BAD_PARAMETER;
-
- display_ptr->set_color_mode(mode);
- return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode,
- std::vector<RenderIntent>* outIntents) {
- return Error::NONE;
-}
-
-Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) {
- return Error::NONE;
-}
-
-Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width,
- uint32_t height, PixelFormat format,
- Dataspace dataspace) {
- return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferAttributes_2_3(Display display,
- PixelFormat* outFormat,
- Dataspace* outDataspace) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) {
- int error = 0;
- auto display_client = display::DisplayClient::Create(&error);
- if (!display_client) {
- ALOGE("Could not connect to display service : %s(%d)", strerror(error),
- error);
- return Error::BAD_CONFIG;
- }
- auto edid_data = display_client->GetConfigurationData(
- display::ConfigFileType::kDeviceEdid);
- auto display_identification_port =
- display_client->GetDisplayIdentificationPort();
- *outPort = display_identification_port.get();
-
- std::copy(edid_data.get().begin(), edid_data.get().end(),
- std::back_inserter(*outData));
- return Error::NONE;
-}
-
-Error VrHwc::setLayerColorTransform(Display display, Layer layer,
- const float* matrix) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSamplingAttributes(
- Display display, PixelFormat& format, Dataspace& dataspace,
- hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
- return Error::NONE;
-}
-
-Error VrHwc::setDisplayedContentSamplingEnabled(
- Display display, IComposerClient::DisplayedContentSampling enable,
- hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
- uint64_t maxFrames) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames,
- uint64_t timestamp, uint64_t& frameCount,
- hidl_vec<uint64_t>& sampleComponent0,
- hidl_vec<uint64_t>& sampleComponent1,
- hidl_vec<uint64_t>& sampleComponent2,
- hidl_vec<uint64_t>& sampleComponent3) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayCapabilities(
- Display display,
- std::vector<IComposerClient::DisplayCapability>* outCapabilities) {
- return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) {
- return Error::NONE;
-}
-
-Error VrHwc::setDisplayBrightness(Display display, float brightness) {
- return Error::NONE;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
deleted file mode 100644
index 3e3a630..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-
-#include <android-base/unique_fd.h>
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <android/hardware/graphics/composer/2.3/IComposer.h>
-#include <composer-hal/2.3/ComposerHal.h>
-#include <private/dvr/vsync_service.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/StrongPointer.h>
-
-#include <mutex>
-#include <unordered_map>
-
-using namespace android::frameworks::vr::composer::V2_0;
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::hardware::hidl_bitfield;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::graphics::composer::V2_1::Config;
-using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
-using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_3::IComposerClient;
-
-namespace android {
-
-class Fence;
-
-namespace dvr {
-
-class VrComposerClient;
-
-using android::hardware::graphics::composer::V2_3::hal::ComposerHal;
-
-namespace types = android::hardware::graphics::common;
-
-using types::V1_1::RenderIntent;
-using types::V1_2::ColorMode;
-using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
-using types::V1_2::PixelFormat;
-
-class ComposerView {
- public:
- struct ComposerLayer {
- using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect;
- using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect;
- using BlendMode =
- hardware::graphics::composer::V2_3::IComposerClient::BlendMode;
-
- Layer id;
- sp<GraphicBuffer> buffer;
- sp<Fence> fence;
- Recti display_frame;
- Rectf crop;
- BlendMode blend_mode;
- float alpha;
- uint32_t type;
- uint32_t app_id;
- uint32_t z_order;
- int32_t cursor_x;
- int32_t cursor_y;
- IComposerClient::Color color;
- int32_t dataspace;
- int32_t transform;
- std::vector<hwc_rect_t> visible_regions;
- std::vector<hwc_rect_t> damaged_regions;
- };
-
- struct Frame {
- Display display_id;
- // This is set to true to notify the upper layer that the display is
- // being removed, or left false in the case of a normal frame. The upper
- // layer tracks display IDs and will handle new ones showing up.
- bool removed = false;
- int32_t display_width;
- int32_t display_height;
- Config active_config;
- ColorMode color_mode;
- IComposerClient::PowerMode power_mode;
- IComposerClient::Vsync vsync_enabled;
- float color_transform[16];
- int32_t color_transform_hint;
- std::vector<ComposerLayer> layers;
- };
-
- class Observer {
- public:
- virtual ~Observer() {}
-
- // Returns a list of layers that need to be shown together. Layers are
- // returned in z-order, with the lowest layer first.
- virtual base::unique_fd OnNewFrame(const Frame& frame) = 0;
- };
-
- virtual ~ComposerView() {}
-
- virtual void ForceDisplaysRefresh() = 0;
- virtual void RegisterObserver(Observer* observer) = 0;
- virtual void UnregisterObserver(Observer* observer) = 0;
-};
-
-struct HwcLayer {
- using Composition =
- hardware::graphics::composer::V2_3::IComposerClient::Composition;
-
- explicit HwcLayer(Layer new_id) { info.id = new_id; }
-
- void dumpDebugInfo(std::string* result) const;
-
- Composition composition_type;
- ComposerView::ComposerLayer info;
- IVrComposerClient::BufferMetadata buffer_metadata;
-};
-
-class HwcDisplay {
- public:
- HwcDisplay(int32_t width, int32_t height);
- ~HwcDisplay();
-
- int32_t width() const { return width_; }
- int32_t height() const { return height_; }
-
- HwcLayer* CreateLayer();
- bool DestroyLayer(Layer id);
- HwcLayer* GetLayer(Layer id);
-
- bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence);
- void SetClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
-
- void GetChangedCompositionTypes(
- std::vector<Layer>* layer_ids,
- std::vector<IComposerClient::Composition>* composition);
-
- Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
-
- std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers();
-
- Config active_config() const { return active_config_; }
- void set_active_config(Config config) { active_config_ = config; }
-
- ColorMode color_mode() const { return color_mode_; }
- void set_color_mode(ColorMode mode) { color_mode_ = mode; }
-
- IComposerClient::PowerMode power_mode() const { return power_mode_; }
- void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
-
- bool vsync_enabled() const { return vsync_enabled_; }
- void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
-
- const float* color_transform() const { return color_transform_; }
- int32_t color_transform_hint() const { return color_transform_hint_; }
- void SetColorTransform(const float* matrix, int32_t hint);
-
- void dumpDebugInfo(std::string* result) const;
-
- private:
- // The client target buffer and the associated fence.
- sp<GraphicBuffer> buffer_;
- IVrComposerClient::BufferMetadata buffer_metadata_;
- sp<Fence> fence_;
-
- // List of currently active layers.
- std::vector<HwcLayer> layers_;
-
- std::vector<Layer> last_frame_layers_ids_;
-
- // Layer ID generator.
- uint64_t layer_ids_ = 1;
-
- int32_t width_;
- int32_t height_;
-
- Config active_config_;
- ColorMode color_mode_;
- IComposerClient::PowerMode power_mode_;
- bool vsync_enabled_ = false;
- float color_transform_[16];
- int32_t color_transform_hint_;
-
- HwcDisplay(const HwcDisplay&) = delete;
- void operator=(const HwcDisplay&) = delete;
-};
-
-class VrHwc : public IComposer, public ComposerHal, public ComposerView {
- public:
- VrHwc();
- ~VrHwc() override;
-
- Error setLayerInfo(Display display, Layer layer, uint32_t type,
- uint32_t appId);
- Error setClientTargetMetadata(
- Display display, const IVrComposerClient::BufferMetadata& metadata);
- Error setLayerBufferMetadata(
- Display display, Layer layer,
- const IVrComposerClient::BufferMetadata& metadata);
-
- // composer::V2_1::ComposerHal
- bool hasCapability(hwc2_capability_t capability) override;
-
- std::string dumpDebugInfo() override { return {}; }
-
- void registerEventCallback(ComposerHal::EventCallback* callback) override;
- void unregisterEventCallback() override;
-
- uint32_t getMaxVirtualDisplayCount() override;
- Error destroyVirtualDisplay(Display display) override;
-
- Error createLayer(Display display, Layer* outLayer) override;
- Error destroyLayer(Display display, Layer layer) override;
-
- Error getActiveConfig(Display display, Config* outConfig) override;
- Error getDisplayAttribute(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) override;
- Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
- Error getDisplayName(Display display, hidl_string* outName) override;
- Error getDisplayType(Display display,
- IComposerClient::DisplayType* outType) override;
- Error getDozeSupport(Display display, bool* outSupport) override;
-
- Error setActiveConfig(Display display, Config config) override;
- Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
- Error setColorTransform(Display display, const float* matrix,
- int32_t hint) override;
- Error setClientTarget(Display display, buffer_handle_t target,
- int32_t acquireFence, int32_t dataspace,
- const std::vector<hwc_rect_t>& damage) override;
- Error setOutputBuffer(Display display, buffer_handle_t buffer,
- int32_t releaseFence) override;
- Error validateDisplay(
- Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks) override;
- Error acceptDisplayChanges(Display display) override;
- Error presentDisplay(Display display, int32_t* outPresentFence,
- std::vector<Layer>* outLayers,
- std::vector<int32_t>* outReleaseFences) override;
-
- Error setLayerCursorPosition(Display display, Layer layer, int32_t x,
- int32_t y) override;
- Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
- int32_t acquireFence) override;
- Error setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<hwc_rect_t>& damage) override;
- Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
- Error setLayerColor(Display display, Layer layer,
- IComposerClient::Color color) override;
- Error setLayerCompositionType(Display display, Layer layer,
- int32_t type) override;
- Error setLayerDataspace(Display display, Layer layer,
- int32_t dataspace) override;
- Error setLayerDisplayFrame(Display display, Layer layer,
- const hwc_rect_t& frame) override;
- Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
- Error setLayerSidebandStream(Display display, Layer layer,
- buffer_handle_t stream) override;
- Error setLayerSourceCrop(Display display, Layer layer,
- const hwc_frect_t& crop) override;
- Error setLayerTransform(Display display, Layer layer,
- int32_t transform) override;
- Error setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<hwc_rect_t>& visible) override;
- Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
- // composer::V2_2::ComposerHal
- Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
- android::base::unique_fd fenceFd) override;
- Error getReadbackBufferFence(Display display,
- android::base::unique_fd* outFenceFd) override;
- Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
- types::V1_1::PixelFormat* format,
- Display* outDisplay) override;
- Error setPowerMode_2_2(Display display,
- IComposerClient::PowerMode mode) override;
- Error setLayerFloatColor(Display display, Layer layer,
- IComposerClient::FloatColor color) override;
- Error getRenderIntents(Display display, types::V1_1::ColorMode mode,
- std::vector<RenderIntent>* outIntents) override;
- std::array<float, 16> getDataspaceSaturationMatrix(
- types::V1_1::Dataspace dataspace) override;
-
- // composer::V2_3::ComposerHal
- Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes,
- float* outMaxLuminance,
- float* outMaxAverageLuminance,
- float* outMinLuminance) override;
- Error setLayerPerFrameMetadata_2_3(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadata>& metadata) override;
- Error getPerFrameMetadataKeys_2_3(
- Display display,
- std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
- Error setColorMode_2_3(Display display, ColorMode mode,
- RenderIntent intent) override;
- Error getRenderIntents_2_3(Display display, ColorMode mode,
- std::vector<RenderIntent>* outIntents) override;
- Error getColorModes_2_3(Display display,
- hidl_vec<ColorMode>* outModes) override;
- Error getClientTargetSupport_2_3(Display display, uint32_t width,
- uint32_t height, PixelFormat format,
- Dataspace dataspace) override;
- Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat,
- Dataspace* outDataspace) override;
- Error getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) override;
- Error setLayerColorTransform(Display display, Layer layer,
- const float* matrix) override;
- Error getDisplayedContentSamplingAttributes(
- Display display, PixelFormat& format, Dataspace& dataspace,
- hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask)
- override;
- Error setDisplayedContentSamplingEnabled(
- Display display, IComposerClient::DisplayedContentSampling enable,
- hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
- uint64_t maxFrames) override;
- Error getDisplayedContentSample(
- Display display, uint64_t maxFrames, uint64_t timestamp,
- uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
- hidl_vec<uint64_t>& sampleComponent1,
- hidl_vec<uint64_t>& sampleComponent2,
- hidl_vec<uint64_t>& sampleComponent3) override;
- Error getDisplayCapabilities(Display display,
- std::vector<IComposerClient::DisplayCapability>*
- outCapabilities) override;
- Error setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override;
- Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
- Error setDisplayBrightness(Display display, float brightness) override;
-
- // IComposer:
- Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
- Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
- Return<void> createClient(createClient_cb hidl_cb) override;
- Return<void> createClient_2_3(
- IComposer::createClient_2_3_cb hidl_cb) override;
-
- // ComposerView:
- void ForceDisplaysRefresh() override;
- void RegisterObserver(Observer* observer) override;
- void UnregisterObserver(Observer* observer) override;
-
- Return<void> debug(const hidl_handle& fd,
- const hidl_vec<hidl_string>& args) override;
-
- private:
- class VsyncCallback : public BnVsyncCallback {
- public:
- status_t onVsync(int64_t vsync_timestamp) override;
- void SetEventCallback(EventCallback* callback);
- private:
- std::mutex mutex_;
- EventCallback* callback_;
- };
-
- HwcDisplay* FindDisplay(Display display);
-
- // Re-evaluate whether or not we should start making onVsync() callbacks to
- // the client. We need enableCallback(true) to have been called, and
- // setVsyncEnabled() to have been called for the primary display. The caller
- // must have mutex_ locked already.
- void UpdateVsyncCallbackEnabledLocked();
-
- wp<VrComposerClient> client_;
-
- // Guard access to internal state from binder threads.
- std::mutex mutex_;
-
- std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
- Display display_count_ = 2;
-
- EventCallback* event_callback_ = nullptr;
- Observer* observer_ = nullptr;
-
- sp<VsyncCallback> vsync_callback_;
-
- VrHwc(const VrHwc&) = delete;
- void operator=(const VrHwc&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp
deleted file mode 100644
index 2e70928..0000000
--- a/services/vr/hardware_composer/tests/vr_composer_test.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#include <android/dvr/BnVrComposerCallback.h>
-#include <binder/IServiceManager.h>
-#include <gtest/gtest.h>
-#include <sys/eventfd.h>
-#include <vr_composer.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-const char kVrDisplayName[] = "VrDisplay_Test";
-
-class TestComposerView : public ComposerView {
- public:
- TestComposerView() {}
- ~TestComposerView() override = default;
-
- size_t display_refresh_count() const { return display_refresh_count_; }
-
- void ForceDisplaysRefresh() override { display_refresh_count_++; }
- void RegisterObserver(Observer* observer) override {}
- void UnregisterObserver(Observer* observer) override {}
-
- TestComposerView(const TestComposerView&) = delete;
- void operator=(const TestComposerView&) = delete;
-
- private:
- size_t display_refresh_count_ = 0;
-};
-
-class TestComposerCallback : public BnVrComposerCallback {
- public:
- TestComposerCallback() {}
- ~TestComposerCallback() override = default;
-
- ComposerView::Frame last_frame() const { return last_frame_; }
-
- binder::Status onNewFrame(
- const ParcelableComposerFrame& frame,
- ParcelableUniqueFd* /* fence */) override {
- last_frame_ = frame.frame();
- return binder::Status::ok();
- }
-
- private:
- ComposerView::Frame last_frame_;
-
- TestComposerCallback(const TestComposerCallback&) = delete;
- void operator=(const TestComposerCallback&) = delete;
-};
-
-class TestComposerCallbackWithFence : public TestComposerCallback {
- public:
- ~TestComposerCallbackWithFence() override = default;
-
- binder::Status onNewFrame(
- const ParcelableComposerFrame& frame,
- ParcelableUniqueFd* fence) override {
- binder::Status status = TestComposerCallback::onNewFrame(frame, fence);
-
- base::unique_fd fd(eventfd(0, 0));
- EXPECT_LE(0, fd.get());
- fence->set_fence(fd);
-
- return status;
- }
-};
-
-sp<GraphicBuffer> CreateBuffer() {
- return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_HW_TEXTURE);
-}
-
-} // namespace
-
-class VrComposerTest : public testing::Test {
- public:
- VrComposerTest() : composer_(new VrComposer(&composer_view_)) {}
- ~VrComposerTest() override = default;
-
- sp<IVrComposer> GetComposerProxy() const {
- sp<IServiceManager> sm(defaultServiceManager());
- return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName)));
- }
-
- void SetUp() override {
- sp<IServiceManager> sm(defaultServiceManager());
- EXPECT_EQ(OK,
- sm->addService(String16(kVrDisplayName), composer_, false));
- }
-
- protected:
- TestComposerView composer_view_;
- sp<VrComposer> composer_;
-
- VrComposerTest(const VrComposerTest&) = delete;
- void operator=(const VrComposerTest&) = delete;
-};
-
-TEST_F(VrComposerTest, TestWithoutObserver) {
- sp<IVrComposer> composer = GetComposerProxy();
- ComposerView::Frame frame;
-
- base::unique_fd fence = composer_->OnNewFrame(frame);
- ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithObserver) {
- sp<IVrComposer> composer = GetComposerProxy();
- sp<TestComposerCallback> callback = new TestComposerCallback();
- ASSERT_EQ(0, composer_view_.display_refresh_count());
- ASSERT_TRUE(composer->registerObserver(callback).isOk());
- ASSERT_EQ(1, composer_view_.display_refresh_count());
-
- ComposerView::Frame frame;
- base::unique_fd fence = composer_->OnNewFrame(frame);
- ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithOneLayer) {
- sp<IVrComposer> composer = GetComposerProxy();
- sp<TestComposerCallback> callback = new TestComposerCallbackWithFence();
- ASSERT_TRUE(composer->registerObserver(callback).isOk());
-
- ComposerView::Frame frame;
- frame.display_id = 1;
- frame.removed = false;
- frame.display_width = 600;
- frame.display_height = 400;
- frame.layers.push_back(ComposerView::ComposerLayer{
- .id = 1,
- .buffer = CreateBuffer(),
- .fence = new Fence(eventfd(0, 0)),
- .display_frame = {0, 0, 600, 400},
- .crop = {0.0f, 0.0f, 600.0f, 400.0f},
- .blend_mode = IComposerClient::BlendMode::NONE,
- .alpha = 1.0f,
- .type = 1,
- .app_id = 1,
- });
- base::unique_fd fence = composer_->OnNewFrame(frame);
- ASSERT_LE(0, fence.get());
-
- ComposerView::Frame received_frame = callback->last_frame();
- ASSERT_EQ(frame.display_id, received_frame.display_id);
- ASSERT_EQ(frame.display_width, received_frame.display_width);
- ASSERT_EQ(frame.display_height, received_frame.display_height);
- ASSERT_EQ(frame.removed, received_frame.removed);
- ASSERT_EQ(1u, received_frame.layers.size());
- ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id);
- ASSERT_NE(nullptr, received_frame.layers[0].buffer.get());
- ASSERT_TRUE(received_frame.layers[0].fence->isValid());
- ASSERT_EQ(frame.layers[0].display_frame.left,
- received_frame.layers[0].display_frame.left);
- ASSERT_EQ(frame.layers[0].display_frame.top,
- received_frame.layers[0].display_frame.top);
- ASSERT_EQ(frame.layers[0].display_frame.right,
- received_frame.layers[0].display_frame.right);
- ASSERT_EQ(frame.layers[0].display_frame.bottom,
- received_frame.layers[0].display_frame.bottom);
- ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left);
- ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top);
- ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right);
- ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom);
- ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode);
- ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha);
- ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type);
- ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp
deleted file mode 100644
index d93f370..0000000
--- a/services/vr/hardware_composer/vr_composer.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "vr_composer.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionCache.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-bool CheckPermission() {
- const android::IPCThreadState* ipc = android::IPCThreadState::self();
- const pid_t pid = ipc->getCallingPid();
- const uid_t uid = ipc->getCallingUid();
- const bool permission = PermissionCache::checkPermission(
- String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid);
- if (!permission)
- ALOGE("permission denied to pid=%d uid=%u", pid, uid);
-
- return permission;
-}
-
-} // namespace
-
-VrComposer::VrComposer(ComposerView* composer_view)
- : composer_view_(composer_view) {
- composer_view_->RegisterObserver(this);
-}
-
-VrComposer::~VrComposer() {
- composer_view_->UnregisterObserver(this);
-}
-
-binder::Status VrComposer::registerObserver(
- const sp<IVrComposerCallback>& callback) {
- {
- std::lock_guard<std::mutex> guard(mutex_);
-
- if (!CheckPermission())
- return binder::Status::fromStatusT(PERMISSION_DENIED);
-
- if (callback_.get()) {
- ALOGE("Failed to register callback, already registered");
- return binder::Status::fromStatusT(ALREADY_EXISTS);
- }
-
- callback_ = callback;
- IInterface::asBinder(callback_)->linkToDeath(this);
- }
-
- // Don't take the lock to force display refresh otherwise it could end in a
- // deadlock since HWC calls this with new frames and it has a lock of its own
- // to serialize access to the display information.
- composer_view_->ForceDisplaysRefresh();
- return binder::Status::ok();
-}
-
-binder::Status VrComposer::clearObserver() {
- std::lock_guard<std::mutex> guard(mutex_);
- callback_ = nullptr;
- return binder::Status::ok();
-}
-
-base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- if (!callback_.get())
- return base::unique_fd();
-
- ParcelableComposerFrame parcelable_frame(frame);
- ParcelableUniqueFd fence;
- binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence);
- if (!ret.isOk())
- ALOGE("Failed to send new frame: %s", ret.toString8().string());
-
- return fence.fence();
-}
-
-void VrComposer::binderDied(const wp<IBinder>& /* who */) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- callback_ = nullptr;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h
deleted file mode 100644
index 1273352..0000000
--- a/services/vr/hardware_composer/vr_composer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-
-#include <android/dvr/BnVrComposer.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class VrComposerCallback;
-
-// Implementation of the IVrComposer service used to notify VR Window Manager
-// when SurfaceFlinger presents 2D UI changes.
-//
-// VR HWC updates the presented frame via the ComposerView::Observer interface.
-// On notification |callback_| is called to update VR Window Manager.
-// NOTE: If VR Window Manager isn't connected, the notification is a no-op.
-class VrComposer
- : public BnVrComposer,
- public ComposerView::Observer,
- public IBinder::DeathRecipient {
- public:
- explicit VrComposer(ComposerView* composer_view);
- ~VrComposer() override;
-
- // BnVrComposer:
- binder::Status registerObserver(
- const sp<IVrComposerCallback>& callback) override;
-
- binder::Status clearObserver() override;
-
- // ComposerView::Observer:
- base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
-
- private:
- // IBinder::DeathRecipient:
- void binderDied(const wp<IBinder>& who) override;
-
- std::mutex mutex_;
-
- sp<IVrComposerCallback> callback_;
-
- ComposerView* composer_view_; // Not owned.
-
- VrComposer(const VrComposer&) = delete;
- void operator=(const VrComposer&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index ba98696..40cf9fb 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -49,7 +49,13 @@
* in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
* pNext chain of VkBindImageMemoryInfo and passed down to the driver.
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
+ *
+ * This version of the extension is largely designed to clean up the mix of
+ * GrallocUsage and GrallocUsage2
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -61,6 +67,8 @@
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \
VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \
+ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3)
/* clang-format off */
typedef enum VkSwapchainImageUsageFlagBitsANDROID {
@@ -90,6 +98,7 @@
* format: gralloc format requested when the buffer was allocated
* 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
*/
typedef struct {
VkStructureType sType;
@@ -98,7 +107,8 @@
int stride;
int format;
int usage; /* DEPRECATED in SPEC_VERSION 6 */
- VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */
+ VkNativeBufferUsage2ANDROID usage2; /* DEPRECATED in SPEC_VERSION 9 */
+ uint64_t usage3; /* ADDED in SPEC_VERSION 9 */
} VkNativeBufferANDROID;
/*
@@ -127,6 +137,21 @@
VkBool32 sharedImage;
} VkPhysicalDevicePresentationPropertiesANDROID;
+/*
+ * struct VkGrallocUsageInfoANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * format: value specifying the format the image will be created with
+ * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ */
+typedef struct {
+ VkStructureType sType;
+ const void* pNext;
+ VkFormat format;
+ VkImageUsageFlags imageUsage;
+} VkGrallocUsageInfoANDROID;
+
/* DEPRECATED in SPEC_VERSION 6 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
VkDevice device,
@@ -134,7 +159,7 @@
VkImageUsageFlags imageUsage,
int* grallocUsage);
-/* ADDED in SPEC_VERSION 6 */
+/* DEPRECATED in SPEC_VERSION 9 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
VkDevice device,
VkFormat format,
@@ -143,6 +168,12 @@
uint64_t* grallocConsumerUsage,
uint64_t* grallocProducerUsage);
+/* ADDED in SPEC_VERSION 9 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)(
+ VkDevice device,
+ const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage);
+
typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
VkDevice device,
VkImage image,
@@ -167,7 +198,7 @@
int* grallocUsage
);
-/* ADDED in SPEC_VERSION 6 */
+/* DEPRECATED in SPEC_VERSION 9 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
VkDevice device,
VkFormat format,
@@ -177,6 +208,13 @@
uint64_t* grallocProducerUsage
);
+/* ADDED in SPEC_VERSION 9 */
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID(
+ VkDevice device,
+ const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage
+);
+
VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
VkDevice device,
VkImage image,
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
"vkGetPhysicalDeviceMemoryProperties2",
"vkGetPhysicalDeviceMemoryProperties2KHR",
"vkGetPhysicalDeviceMultisamplePropertiesEXT",
+ "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
"vkGetPhysicalDevicePresentRectanglesKHR",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7664518..a99355f 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,6 +1027,51 @@
}
}
+VkResult GetAndroidNativeBufferSpecVersion9Support(
+ VkPhysicalDevice physicalDevice,
+ bool& support) {
+ support = false;
+
+ const InstanceData& data = GetData(physicalDevice);
+
+ // Call to get propertyCount
+ uint32_t propertyCount = 0;
+ ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
+ VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &propertyCount, nullptr);
+ ATRACE_END();
+
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
+ // Call to enumerate properties
+ std::vector<VkExtensionProperties> properties(propertyCount);
+ ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
+ result = data.driver.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &propertyCount, properties.data());
+ ATRACE_END();
+
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
+ for (uint32_t i = 0; i < propertyCount; i++) {
+ auto& prop = properties[i];
+
+ if (strcmp(prop.extensionName,
+ VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+ continue;
+
+ if (prop.specVersion >= 9) {
+ support = true;
+ return result;
+ }
+ }
+
+ return result;
+}
+
VkResult EnumerateDeviceExtensionProperties(
VkPhysicalDevice physicalDevice,
const char* pLayerName,
@@ -1061,6 +1106,49 @@
VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
}
+ // Conditionally add VK_EXT_IMAGE_COMPRESSION_CONTROL* if feature and ANB
+ // support is provided by the driver
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT
+ swapchainCompFeats = {};
+ swapchainCompFeats.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
+ swapchainCompFeats.pNext = nullptr;
+ swapchainCompFeats.imageCompressionControlSwapchain = false;
+ VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
+ imageCompFeats.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
+ imageCompFeats.pNext = &swapchainCompFeats;
+ imageCompFeats.imageCompressionControl = false;
+
+ VkPhysicalDeviceFeatures2 feats2 = {};
+ feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ feats2.pNext = &imageCompFeats;
+
+ const auto& driver = GetData(physicalDevice).driver;
+ if (driver.GetPhysicalDeviceFeatures2 ||
+ driver.GetPhysicalDeviceFeatures2KHR) {
+ GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ }
+
+ bool anb9 = false;
+ VkResult result =
+ GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9);
+
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
+ if (anb9 && imageCompFeats.imageCompressionControl) {
+ loader_extensions.push_back(
+ {VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME,
+ VK_EXT_IMAGE_COMPRESSION_CONTROL_SPEC_VERSION});
+ }
+ if (anb9 && swapchainCompFeats.imageCompressionControlSwapchain) {
+ loader_extensions.push_back(
+ {VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME,
+ VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION});
+ }
+
// enumerate our extensions first
if (!pLayerName && pProperties) {
uint32_t count = std::min(
@@ -1078,7 +1166,7 @@
}
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
- VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
ATRACE_END();
@@ -1254,15 +1342,18 @@
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
- // sanity check ANDROID_native_buffer implementation, whose set of
+ // Confirming ANDROID_native_buffer implementation, whose set of
// entrypoints varies according to the spec version.
if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
!data->driver.GetSwapchainGrallocUsageANDROID &&
- !data->driver.GetSwapchainGrallocUsage2ANDROID) {
- ALOGE("Driver's implementation of ANDROID_native_buffer is broken;"
- " must expose at least one of "
- "vkGetSwapchainGrallocUsageANDROID or "
- "vkGetSwapchainGrallocUsage2ANDROID");
+ !data->driver.GetSwapchainGrallocUsage2ANDROID &&
+ !data->driver.GetSwapchainGrallocUsage3ANDROID) {
+ ALOGE(
+ "Driver's implementation of ANDROID_native_buffer is broken;"
+ " must expose at least one of "
+ "vkGetSwapchainGrallocUsageANDROID or "
+ "vkGetSwapchainGrallocUsage2ANDROID or "
+ "vkGetSwapchainGrallocUsage3ANDROID");
data->driver.DestroyDevice(dev, pAllocator);
FreeDeviceData(data, data_allocator);
@@ -1441,10 +1532,89 @@
if (driver.GetPhysicalDeviceFeatures2) {
driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+ } else {
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+ }
+
+ // Conditionally add imageCompressionControlSwapchain if
+ // imageCompressionControl is supported Check for imageCompressionControl in
+ // the pChain
+ bool imageCompressionControl = false;
+ bool imageCompressionControlInChain = false;
+ bool imageCompressionControlSwapchainInChain = false;
+ VkPhysicalDeviceFeatures2* pFeats = pFeatures;
+ while (pFeats) {
+ switch (pFeats->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT: {
+ const VkPhysicalDeviceImageCompressionControlFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ const VkPhysicalDeviceImageCompressionControlFeaturesEXT*>(
+ pFeats);
+ imageCompressionControl =
+ compressionFeat->imageCompressionControl;
+ imageCompressionControlInChain = true;
+ } break;
+
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+ pFeats);
+ compressionFeat->imageCompressionControlSwapchain = false;
+ imageCompressionControlSwapchainInChain = true;
+ } break;
+
+ default:
+ break;
+ }
+ pFeats = reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext);
+ }
+
+ if (!imageCompressionControlSwapchainInChain) {
return;
}
- driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+ // If not in pchain, explicitly query for imageCompressionControl
+ if (!imageCompressionControlInChain) {
+ VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
+ imageCompFeats.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
+ imageCompFeats.pNext = nullptr;
+ imageCompFeats.imageCompressionControl = false;
+
+ VkPhysicalDeviceFeatures2 feats2 = {};
+ feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ feats2.pNext = &imageCompFeats;
+
+ if (driver.GetPhysicalDeviceFeatures2) {
+ driver.GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ } else {
+ driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, &feats2);
+ }
+
+ imageCompressionControl = imageCompFeats.imageCompressionControl;
+ }
+
+ // Only enumerate imageCompressionControlSwapchin if imageCompressionControl
+ if (imageCompressionControl) {
+ pFeats = pFeatures;
+ while (pFeats) {
+ switch (pFeats->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+ pFeats);
+ compressionFeat->imageCompressionControlSwapchain = true;
+ } break;
+
+ default:
+ break;
+ }
+ pFeats =
+ reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext);
+ }
+ }
}
void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 14c516b..4d2bbd6 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -107,6 +107,8 @@
VkPhysicalDevice physicalDevice,
VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
+bool GetAndroidNativeBufferSpecVersion9Support(VkPhysicalDevice physicalDevice);
+
VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
const char* pName);
VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index b436db1..de98aa7 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -496,6 +496,13 @@
nullptr,
},
{
+ "vkGetSwapchainGrallocUsage3ANDROID",
+ ProcHook::DEVICE,
+ ProcHook::ANDROID_native_buffer,
+ nullptr,
+ nullptr,
+ },
+ {
"vkGetSwapchainGrallocUsageANDROID",
ProcHook::DEVICE,
ProcHook::ANDROID_native_buffer,
@@ -664,6 +671,7 @@
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+ INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
// clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 079f9cc..2f60086 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -123,6 +123,7 @@
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
+ PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID;
PFN_vkAcquireImageANDROID AcquireImageANDROID;
PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
// clang-format on
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index f49e8f3..b189c68 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -178,6 +178,7 @@
vkGetImageSparseMemoryRequirements;
vkGetImageSparseMemoryRequirements2; # introduced=28
vkGetImageSubresourceLayout;
+ vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake
vkGetInstanceProcAddr;
vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28
vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 9c6d19f..c7284ce 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -243,6 +243,11 @@
// syncronous requests to Surface Flinger):
enum { MIN_NUM_FRAMES_AGO = 5 };
+bool IsSharedPresentMode(VkPresentModeKHR mode) {
+ return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+ mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
+}
+
struct Swapchain {
Swapchain(Surface& surface_,
uint32_t num_images_,
@@ -254,9 +259,7 @@
pre_transform(pre_transform_),
frame_timestamps_enabled(false),
acquire_next_image_timeout(-1),
- shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
- present_mode ==
- VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+ shared(IsSharedPresentMode(present_mode)) {
ANativeWindow* window = surface.window.get();
native_window_get_refresh_cycle_duration(
window,
@@ -668,10 +671,11 @@
// VkSurfaceProtectedCapabilitiesKHR::supportsProtected. The following
// four values cannot be known without a surface. Default values will
// be supplied anyway, but cannot be relied upon.
- width = 1000;
- height = 1000;
- transform_hint = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
- max_buffer_count = 10;
+ width = 0xFFFFFFFF;
+ height = 0xFFFFFFFF;
+ transform_hint = VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
+ capabilities->minImageCount = 0xFFFFFFFF;
+ capabilities->maxImageCount = 0xFFFFFFFF;
} else {
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
@@ -703,9 +707,9 @@
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
+ capabilities->minImageCount = std::min(max_buffer_count, 3);
+ capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
}
- capabilities->minImageCount = std::min(max_buffer_count, 3);
- capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
capabilities->currentExtent =
VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
@@ -714,6 +718,17 @@
capabilities->minImageExtent = VkExtent2D{1, 1};
capabilities->maxImageExtent = VkExtent2D{4096, 4096};
+ if (capabilities->maxImageExtent.height <
+ capabilities->currentExtent.height) {
+ capabilities->maxImageExtent.height =
+ capabilities->currentExtent.height;
+ }
+
+ if (capabilities->maxImageExtent.width <
+ capabilities->currentExtent.width) {
+ capabilities->maxImageExtent.width = capabilities->currentExtent.width;
+ }
+
capabilities->maxImageArrayLayers = 1;
capabilities->supportedTransforms = kSupportedTransforms;
@@ -742,7 +757,6 @@
const InstanceData& instance_data = GetData(pdev);
- bool wide_color_support = false;
uint64_t consumer_usage = 0;
bool colorspace_ext =
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
@@ -753,27 +767,15 @@
if (!surfaceless_enabled) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- // Support for VK_GOOGLE_surfaceless_query. The EGL loader
- // unconditionally supports wide color formats, even if they will cause
- // a SurfaceFlinger fallback. Based on that, wide_color_support will be
- // set to true in this case.
- wide_color_support = true;
+ // Support for VK_GOOGLE_surfaceless_query.
// TODO(b/203826952): research proper value; temporarily use the
// values seen on Pixel
consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
} else {
Surface& surface = *SurfaceFromHandle(surface_handle);
- int err = native_window_get_wide_color_support(surface.window.get(),
- &wide_color_support);
- if (err) {
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- ALOGV("wide_color_support is: %d", wide_color_support);
-
consumer_usage = surface.consumer_usage;
}
- wide_color_support = wide_color_support && colorspace_ext;
AHardwareBuffer_Desc desc = {};
desc.width = 1;
@@ -786,17 +788,15 @@
std::vector<VkSurfaceFormatKHR> all_formats = {
{VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- // Also allow to use PASS_THROUGH + HAL_DATASPACE_ARBITRARY
- {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT},
- {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT},
};
if (colorspace_ext) {
all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
- }
-
- if (wide_color_support) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -811,17 +811,21 @@
if (AHardwareBuffer_isSupported(&desc)) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ if (colorspace_ext) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R5G6B5_UNORM_PACK16,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ }
}
desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
if (AHardwareBuffer_isSupported(&desc)) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_PASS_THROUGH_EXT});
- if (wide_color_support) {
+ if (colorspace_ext) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
@@ -836,10 +840,10 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
- all_formats.emplace_back(
- VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
- VK_COLOR_SPACE_PASS_THROUGH_EXT});
- if (wide_color_support) {
+ if (colorspace_ext) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
@@ -848,9 +852,10 @@
desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM;
if (AHardwareBuffer_isSupported(&desc)) {
- all_formats.emplace_back(
- VkSurfaceFormatKHR{VK_FORMAT_R8_UNORM,
- VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ if (colorspace_ext) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ }
}
// NOTE: Any new formats that are added must be coordinated across different
@@ -928,25 +933,87 @@
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
pSurfaceFormatCount, nullptr);
- } else {
- // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
- // after the call.
- std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
- VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
- physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
- surface_formats.data());
+ }
- if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
- // marshal results individually due to stride difference.
- // completely ignore any chained extension structs.
- uint32_t formats_to_marshal = *pSurfaceFormatCount;
- for (uint32_t i = 0u; i < formats_to_marshal; i++) {
- pSurfaceFormats[i].surfaceFormat = surface_formats[i];
- }
- }
+ // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+ // after the call.
+ std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
+ VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+ physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+ surface_formats.data());
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
return result;
}
+
+ const auto& driver = GetData(physicalDevice).driver;
+
+ // marshal results individually due to stride difference.
+ uint32_t formats_to_marshal = *pSurfaceFormatCount;
+ for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+ pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+ // Query the compression properties for the surface format
+ VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i];
+ while (pSurfaceFormat->pNext) {
+ pSurfaceFormat =
+ reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext);
+ switch (pSurfaceFormat->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: {
+ VkImageCompressionPropertiesEXT* surfaceCompressionProps =
+ reinterpret_cast<VkImageCompressionPropertiesEXT*>(
+ pSurfaceFormat);
+
+ if (surfaceCompressionProps &&
+ driver.GetPhysicalDeviceImageFormatProperties2KHR) {
+ VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {};
+ imageFormatInfo.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
+ imageFormatInfo.format =
+ pSurfaceFormats[i].surfaceFormat.format;
+ imageFormatInfo.pNext = nullptr;
+
+ VkImageCompressionControlEXT compressionControl = {};
+ compressionControl.sType =
+ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT;
+ compressionControl.pNext = imageFormatInfo.pNext;
+
+ imageFormatInfo.pNext = &compressionControl;
+
+ VkImageCompressionPropertiesEXT compressionProps = {};
+ compressionProps.sType =
+ VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT;
+ compressionProps.pNext = nullptr;
+
+ VkImageFormatProperties2KHR imageFormatProps = {};
+ imageFormatProps.sType =
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
+ imageFormatProps.pNext = &compressionProps;
+
+ VkResult compressionRes =
+ driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ physicalDevice, &imageFormatInfo,
+ &imageFormatProps);
+ if (compressionRes == VK_SUCCESS) {
+ surfaceCompressionProps->imageCompressionFlags =
+ compressionProps.imageCompressionFlags;
+ surfaceCompressionProps
+ ->imageCompressionFixedRateFlags =
+ compressionProps.imageCompressionFixedRateFlags;
+ } else {
+ return compressionRes;
+ }
+ }
+ } break;
+
+ default:
+ // Ignore all other extension structs
+ break;
+ }
+ }
+ }
+
+ return result;
}
VKAPI_ATTR
@@ -1359,8 +1426,48 @@
num_images = 1;
}
+ void* usage_info_pNext = nullptr;
+ VkImageCompressionControlEXT image_compression = {};
uint64_t native_usage = 0;
- if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+ VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ 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;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
uint64_t consumer_usage, producer_usage;
ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
result = dispatch.GetSwapchainGrallocUsage2ANDROID(
@@ -1372,7 +1479,7 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
native_usage =
- convertGralloc1ToBufferUsage(consumer_usage, producer_usage);
+ convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
} else if (dispatch.GetSwapchainGrallocUsageANDROID) {
ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
int32_t legacy_usage = 0;
@@ -1426,7 +1533,7 @@
#pragma clang diagnostic ignored "-Wold-style-cast"
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
#pragma clang diagnostic pop
- .pNext = nullptr,
+ .pNext = usage_info_pNext,
.usage = swapchain_image_usage,
};
VkNativeBufferANDROID image_native_buffer = {
@@ -1484,6 +1591,7 @@
android_convertGralloc0To1Usage(int(img.buffer->usage),
&image_native_buffer.usage2.producer,
&image_native_buffer.usage2.consumer);
+ image_native_buffer.usage3 = img.buffer->usage;
ATRACE_BEGIN("CreateImage");
result =
@@ -1691,6 +1799,173 @@
return a != VK_SUCCESS ? a : b;
}
+// KHR_incremental_present aspect of QueuePresentKHR
+static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) {
+ std::vector<android_native_rect_t> rects(pRegion->rectangleCount);
+ for (auto i = 0u; i < pRegion->rectangleCount; i++) {
+ auto const& rect = pRegion->pRectangles[i];
+ if (rect.layer > 0) {
+ ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead",
+ rect.layer);
+ }
+
+ rects[i].left = rect.offset.x;
+ rects[i].bottom = rect.offset.y;
+ rects[i].right = rect.offset.x + rect.extent.width;
+ rects[i].top = rect.offset.y + rect.extent.height;
+ }
+ native_window_set_surface_damage(window, rects.data(), rects.size());
+}
+
+// GOOGLE_display_timing aspect of QueuePresentKHR
+static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) {
+ ANativeWindow *window = swapchain.surface.window.get();
+
+ // We don't know whether the app will actually use GOOGLE_display_timing
+ // with a particular swapchain until QueuePresent; enable it on the BQ
+ // now if needed
+ if (!swapchain.frame_timestamps_enabled) {
+ ALOGV("Calling native_window_enable_frame_timestamps(true)");
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+
+ // Record the nativeFrameId so it can be later correlated to
+ // this present.
+ uint64_t nativeFrameId = 0;
+ int err = native_window_get_next_frame_id(
+ window, &nativeFrameId);
+ if (err != android::OK) {
+ ALOGE("Failed to get next native frame ID.");
+ }
+
+ // Add a new timing record with the user's presentID and
+ // the nativeFrameId.
+ swapchain.timing.emplace_back(pTime, nativeFrameId);
+ if (swapchain.timing.size() > MAX_TIMING_INFOS) {
+ swapchain.timing.erase(
+ swapchain.timing.begin(),
+ swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS);
+ }
+ if (pTime->desiredPresentTime) {
+ ALOGV(
+ "Calling native_window_set_buffers_timestamp(%" PRId64 ")",
+ pTime->desiredPresentTime);
+ native_window_set_buffers_timestamp(
+ window,
+ static_cast<int64_t>(pTime->desiredPresentTime));
+ }
+}
+
+static VkResult PresentOneSwapchain(
+ VkQueue queue,
+ Swapchain& swapchain,
+ uint32_t imageIndex,
+ const VkPresentRegionKHR *pRegion,
+ const VkPresentTimeGOOGLE *pTime,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore *pWaitSemaphores) {
+
+ VkDevice device = GetData(queue).driver_device;
+ const auto& dispatch = GetData(queue).driver;
+
+ Swapchain::Image& img = swapchain.images[imageIndex];
+ VkResult swapchain_result = VK_SUCCESS;
+ VkResult result;
+ int err;
+
+ // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the
+ // wait semaphores, so this doesn't actually work for the multiple swapchain
+ // case.
+ int fence = -1;
+ result = dispatch.QueueSignalReleaseImageANDROID(
+ queue, waitSemaphoreCount,
+ pWaitSemaphores, img.image, &fence);
+ if (result != VK_SUCCESS) {
+ ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
+ swapchain_result = result;
+ }
+ if (img.release_fence >= 0)
+ close(img.release_fence);
+ img.release_fence = fence < 0 ? -1 : dup(fence);
+
+ if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) {
+ ANativeWindow* window = swapchain.surface.window.get();
+ if (swapchain_result == VK_SUCCESS) {
+
+ if (pRegion) {
+ SetSwapchainSurfaceDamage(window, pRegion);
+ }
+ if (pTime) {
+ SetSwapchainFrameTimestamp(swapchain, pTime);
+ }
+
+ err = window->queueBuffer(window, img.buffer.get(), fence);
+ // queueBuffer always closes fence, even on error
+ if (err != android::OK) {
+ ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ if (img.dequeue_fence >= 0) {
+ close(img.dequeue_fence);
+ img.dequeue_fence = -1;
+ }
+ img.dequeued = false;
+ }
+
+ // If the swapchain is in shared mode, immediately dequeue the
+ // buffer so it can be presented again without an intervening
+ // call to AcquireNextImageKHR. We expect to get the same buffer
+ // back from every call to dequeueBuffer in this mode.
+ if (swapchain.shared && swapchain_result == VK_SUCCESS) {
+ ANativeWindowBuffer* buffer;
+ int fence_fd;
+ err = window->dequeueBuffer(window, &buffer, &fence_fd);
+ if (err != android::OK) {
+ ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else if (img.buffer != buffer) {
+ ALOGE("got wrong image back for shared swapchain");
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ img.dequeue_fence = fence_fd;
+ img.dequeued = true;
+ }
+ }
+ }
+ if (swapchain_result != VK_SUCCESS) {
+ OrphanSwapchain(device, &swapchain);
+ }
+ // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
+ // and only when the window's transform/rotation changes. Extent
+ // changes will not cause VK_SUBOPTIMAL_KHR because of the
+ // application issues that were caused when the following transform
+ // change was added.
+ int window_transform_hint;
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+ &window_transform_hint);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+ strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ }
+ if (swapchain.pre_transform != window_transform_hint) {
+ swapchain_result =
+ WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
+ }
+ } else {
+ ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
+ img, true);
+ swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ return swapchain_result;
+}
+
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
ATRACE_CALL();
@@ -1699,8 +1974,6 @@
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
- VkDevice device = GetData(queue).driver_device;
- const auto& dispatch = GetData(queue).driver;
VkResult final_result = VK_SUCCESS;
// Look at the pNext chain for supported extension structs:
@@ -1736,175 +2009,19 @@
(present_regions) ? present_regions->pRegions : nullptr;
const VkPresentTimeGOOGLE* times =
(present_times) ? present_times->pTimes : nullptr;
- const VkAllocationCallbacks* allocator = &GetData(device).allocator;
- android_native_rect_t* rects = nullptr;
- uint32_t nrects = 0;
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
Swapchain& swapchain =
*SwapchainFromHandle(present_info->pSwapchains[sc]);
- uint32_t image_idx = present_info->pImageIndices[sc];
- Swapchain::Image& img = swapchain.images[image_idx];
- const VkPresentRegionKHR* region =
- (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr;
- const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr;
- VkResult swapchain_result = VK_SUCCESS;
- VkResult result;
- int err;
- int fence = -1;
- result = dispatch.QueueSignalReleaseImageANDROID(
- queue, present_info->waitSemaphoreCount,
- present_info->pWaitSemaphores, img.image, &fence);
- if (result != VK_SUCCESS) {
- ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
- swapchain_result = result;
- }
- if (img.release_fence >= 0)
- close(img.release_fence);
- img.release_fence = fence < 0 ? -1 : dup(fence);
-
- if (swapchain.surface.swapchain_handle ==
- present_info->pSwapchains[sc]) {
- ANativeWindow* window = swapchain.surface.window.get();
- if (swapchain_result == VK_SUCCESS) {
- if (region) {
- // Process the incremental-present hint for this swapchain:
- uint32_t rcount = region->rectangleCount;
- if (rcount > nrects) {
- android_native_rect_t* new_rects =
- static_cast<android_native_rect_t*>(
- allocator->pfnReallocation(
- allocator->pUserData, rects,
- sizeof(android_native_rect_t) * rcount,
- alignof(android_native_rect_t),
- VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
- if (new_rects) {
- rects = new_rects;
- nrects = rcount;
- } else {
- rcount = 0; // Ignore the hint for this swapchain
- }
- }
- for (uint32_t r = 0; r < rcount; ++r) {
- if (region->pRectangles[r].layer > 0) {
- ALOGV(
- "vkQueuePresentKHR ignoring invalid layer "
- "(%u); using layer 0 instead",
- region->pRectangles[r].layer);
- }
- int x = region->pRectangles[r].offset.x;
- int y = region->pRectangles[r].offset.y;
- int width = static_cast<int>(
- region->pRectangles[r].extent.width);
- int height = static_cast<int>(
- region->pRectangles[r].extent.height);
- android_native_rect_t* cur_rect = &rects[r];
- cur_rect->left = x;
- cur_rect->top = y + height;
- cur_rect->right = x + width;
- cur_rect->bottom = y;
- }
- native_window_set_surface_damage(window, rects, rcount);
- }
- if (time) {
- if (!swapchain.frame_timestamps_enabled) {
- ALOGV(
- "Calling "
- "native_window_enable_frame_timestamps(true)");
- native_window_enable_frame_timestamps(window, true);
- swapchain.frame_timestamps_enabled = true;
- }
-
- // Record the nativeFrameId so it can be later correlated to
- // this present.
- uint64_t nativeFrameId = 0;
- err = native_window_get_next_frame_id(
- window, &nativeFrameId);
- if (err != android::OK) {
- ALOGE("Failed to get next native frame ID.");
- }
-
- // Add a new timing record with the user's presentID and
- // the nativeFrameId.
- swapchain.timing.emplace_back(time, nativeFrameId);
- while (swapchain.timing.size() > MAX_TIMING_INFOS) {
- swapchain.timing.erase(swapchain.timing.begin());
- }
- if (time->desiredPresentTime) {
- // Set the desiredPresentTime:
- ALOGV(
- "Calling "
- "native_window_set_buffers_timestamp(%" PRId64 ")",
- time->desiredPresentTime);
- native_window_set_buffers_timestamp(
- window,
- static_cast<int64_t>(time->desiredPresentTime));
- }
- }
-
- err = window->queueBuffer(window, img.buffer.get(), fence);
- // queueBuffer always closes fence, even on error
- if (err != android::OK) {
- ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- } else {
- if (img.dequeue_fence >= 0) {
- close(img.dequeue_fence);
- img.dequeue_fence = -1;
- }
- img.dequeued = false;
- }
-
- // If the swapchain is in shared mode, immediately dequeue the
- // buffer so it can be presented again without an intervening
- // call to AcquireNextImageKHR. We expect to get the same buffer
- // back from every call to dequeueBuffer in this mode.
- if (swapchain.shared && swapchain_result == VK_SUCCESS) {
- ANativeWindowBuffer* buffer;
- int fence_fd;
- err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != android::OK) {
- ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else if (img.buffer != buffer) {
- ALOGE("got wrong image back for shared swapchain");
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else {
- img.dequeue_fence = fence_fd;
- img.dequeued = true;
- }
- }
- }
- if (swapchain_result != VK_SUCCESS) {
- OrphanSwapchain(device, &swapchain);
- }
- // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
- // and only when the window's transform/rotation changes. Extent
- // changes will not cause VK_SUBOPTIMAL_KHR because of the
- // application issues that were caused when the following transform
- // change was added.
- int window_transform_hint;
- err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
- &window_transform_hint);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
- strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- }
- if (swapchain.pre_transform != window_transform_hint) {
- swapchain_result =
- WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
- }
- } else {
- ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
- img, true);
- swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
- }
+ VkResult swapchain_result = PresentOneSwapchain(
+ queue,
+ swapchain,
+ present_info->pImageIndices[sc],
+ (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr,
+ times ? ×[sc] : nullptr,
+ present_info->waitSemaphoreCount,
+ present_info->pWaitSemaphores);
if (present_info->pResults)
present_info->pResults[sc] = swapchain_result;
@@ -1912,9 +2029,6 @@
if (swapchain_result != final_result)
final_result = WorstPresentResult(final_result, swapchain_result);
}
- if (rects) {
- allocator->pfnFree(allocator->pUserData, rects);
- }
return final_result;
}
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3c91150..f998b1a 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -948,6 +948,17 @@
return VK_SUCCESS;
}
+VkResult GetSwapchainGrallocUsage3ANDROID(
+ VkDevice,
+ const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+ uint64_t* grallocUsage) {
+ // The null driver never reads or writes the gralloc buffer
+ ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__,
+ grallocUsageInfo->format);
+ *grallocUsage = 0;
+ return VK_SUCCESS;
+}
+
VkResult AcquireImageANDROID(VkDevice,
VkImage,
int fence,
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index f6dcf09..0cb7bd3 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -261,6 +261,7 @@
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
{"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
+ {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
{"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 3e003e3..5c7fea0 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -209,6 +209,7 @@
VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 4176509..c25c6cb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -69,6 +69,7 @@
_OPTIONAL_COMMANDS = [
'vkGetSwapchainGrallocUsageANDROID',
'vkGetSwapchainGrallocUsage2ANDROID',
+ 'vkGetSwapchainGrallocUsage3ANDROID',
]
# Dict for mapping dispatch table to a type.