Merge "Add _Nonnull annotations to AImageDecoder"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index a686dfb..560459b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,7 @@
include/powermanager/
libs/binder/fuzzer/
libs/binder/ndk/
+ libs/binder/tests/fuzzers/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 5db44c7..307e21c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -53,6 +53,9 @@
},
{
"include-filter": "*RelativeZTest.*"
+ },
+ {
+ "include-filter": "*RefreshRateOverlayTest.*"
}
]
},
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 2519ffa..3184843 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -99,7 +99,9 @@
/* Tracing categories */
static const TracingCategory k_categories[] = {
- { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } },
+ { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, {
+ { OPT, "events/gpu_mem/gpu_mem_total/enable" },
+ } },
{ "input", "Input", ATRACE_TAG_INPUT, { } },
{ "view", "View System", ATRACE_TAG_VIEW, { } },
{ "webview", "WebView", ATRACE_TAG_WEBVIEW, { } },
@@ -241,6 +243,7 @@
{ OPT, "events/kmem/ion_heap_grow/enable" },
{ OPT, "events/kmem/ion_heap_shrink/enable" },
{ OPT, "events/ion/ion_stat/enable" },
+ { OPT, "events/gpu_mem/gpu_mem_total/enable" },
} },
{ "thermal", "Thermal event", 0, {
{ REQ, "events/thermal/thermal_temperature/enable" },
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 1f055f3..80d14ac 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -106,6 +106,7 @@
defaults: ["dumpstate_defaults"],
srcs: [
"DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"main.cpp",
],
@@ -134,6 +135,7 @@
defaults: ["dumpstate_defaults"],
srcs: [
"DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_test.cpp",
],
@@ -151,6 +153,7 @@
defaults: ["dumpstate_defaults"],
srcs: [
"DumpPool.cpp",
+ "TaskQueue.cpp",
"dumpstate.cpp",
"tests/dumpstate_smoke_test.cpp",
],
diff --git a/cmds/dumpstate/DumpPool.cpp b/cmds/dumpstate/DumpPool.cpp
index e174c8e..e15ac3f 100644
--- a/cmds/dumpstate/DumpPool.cpp
+++ b/cmds/dumpstate/DumpPool.cpp
@@ -100,6 +100,10 @@
}
}
+void DumpPool::deleteTempFiles() {
+ deleteTempFiles(tmp_root_);
+}
+
void DumpPool::setLogDuration(bool log_duration) {
log_duration_ = log_duration;
}
diff --git a/cmds/dumpstate/DumpPool.h b/cmds/dumpstate/DumpPool.h
index a4ea875..0c3c2cc 100644
--- a/cmds/dumpstate/DumpPool.h
+++ b/cmds/dumpstate/DumpPool.h
@@ -134,6 +134,11 @@
*/
void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
+ /*
+ * Deletes temporary files created by DumpPool.
+ */
+ void deleteTempFiles();
+
static const std::string PREFIX_TMPFILE_NAME;
private:
diff --git a/cmds/dumpstate/TaskQueue.cpp b/cmds/dumpstate/TaskQueue.cpp
new file mode 100644
index 0000000..8550aec
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "TaskQueue.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+TaskQueue::~TaskQueue() {
+ run(/* do_cancel = */true);
+}
+
+void TaskQueue::run(bool do_cancel) {
+ std::unique_lock lock(lock_);
+ while (!tasks_.empty()) {
+ auto task = tasks_.front();
+ tasks_.pop();
+ lock.unlock();
+ std::invoke(task, do_cancel);
+ lock.lock();
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/TaskQueue.h b/cmds/dumpstate/TaskQueue.h
new file mode 100644
index 0000000..b7e72f1
--- /dev/null
+++ b/cmds/dumpstate/TaskQueue.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+#define FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
+
+#include <mutex>
+#include <queue>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+/*
+ * A task queue for dumpstate to collect tasks such as adding file to the zip
+ * which are needed to run in a single thread. The task is a callable function
+ * included a cancel task boolean parameter. The TaskQueue could
+ * cancel the task in the destructor if the task has never been called.
+ */
+class TaskQueue {
+ public:
+ TaskQueue() = default;
+ ~TaskQueue();
+
+ /*
+ * Adds a task into the queue.
+ *
+ * |f| Callable function to execute the task. The function must include a
+ * boolean parameter for TaskQueue to notify whether the task is
+ * cancelled or not.
+ * |args| A list of arguments.
+ */
+ template<class F, class... Args> void add(F&& f, Args&&... args) {
+ auto func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
+ std::unique_lock lock(lock_);
+ tasks_.emplace([=](bool cancelled) {
+ std::invoke(func, cancelled);
+ });
+ }
+
+ /*
+ * Invokes all tasks in the task queue.
+ *
+ * |do_cancel| true to cancel all tasks in the queue.
+ */
+ void run(bool do_cancel);
+
+ private:
+ using Task = std::function<void(bool)>;
+
+ std::mutex lock_;
+ std::queue<Task> tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskQueue);
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif //FRAMEWORK_NATIVE_CMD_TASKQUEUE_H_
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 7d195b4..91d5524 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -116,6 +116,7 @@
using android::os::dumpstate::DumpFileToFd;
using android::os::dumpstate::DumpPool;
using android::os::dumpstate::PropertiesHelper;
+using android::os::dumpstate::TaskQueue;
// Keep in sync with
// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -128,8 +129,8 @@
static Dumpstate& ds = Dumpstate::GetInstance();
static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT,
- bool verbose_duration = false) {
- return ds.RunCommand(title, full_command, options, verbose_duration);
+ bool verbose_duration = false, int out_fd = STDOUT_FILENO) {
+ return ds.RunCommand(title, full_command, options, verbose_duration, out_fd);
}
// Reasonable value for max stats.
@@ -212,11 +213,21 @@
RUN_SLOW_FUNCTION_AND_LOG(log_title, func_ptr, __VA_ARGS__); \
RETURN_IF_USER_DENIED_CONSENT();
+#define WAIT_TASK_WITH_CONSENT_CHECK(task_name, pool_ptr) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ pool_ptr->waitForTask(task_name); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
static const char* WAKE_LOCK_NAME = "dumpstate_wakelock";
// Names of parallel tasks, they are used for the DumpPool to identify the dump
// task and the log title of the duration report.
static const std::string DUMP_TRACES_TASK = "DUMP TRACES";
+static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT";
+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 DUMP_APP_INFOS_TASK = "DUMP APP INFOS";
namespace android {
namespace os {
@@ -326,8 +337,12 @@
static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
- long dumpsysTimeoutMs = 0) {
- return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs);
+ long dumpsysTimeoutMs = 0, int out_fd = STDOUT_FILENO) {
+ return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs, out_fd);
+}
+static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ int out_fd) {
+ return ds.RunDumpsys(title, dumpsysArgs, Dumpstate::DEFAULT_DUMPSYS, 0, out_fd);
}
static int DumpFile(const std::string& title, const std::string& path) {
return ds.DumpFile(title, path);
@@ -1014,7 +1029,6 @@
MYLOGD("Not dumping incident report because it's not a zipped bugreport\n");
return;
}
- DurationReporter duration_reporter("INCIDENT REPORT");
const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report";
auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
@@ -1029,9 +1043,11 @@
// Use a different name from "incident.proto"
// /proto/incident.proto is reserved for incident service dump
// i.e. metadata for debugging.
- ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path);
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded(kProtoPath + "incident_report" + kProtoExt,
+ path);
+ } else {
+ unlink(path.c_str());
}
- unlink(path.c_str());
}
static void DumpVisibleWindowViews() {
@@ -1326,15 +1342,21 @@
/* timeout= */ 90s, /* service_timeout= */ 10s);
}
-static void DumpHals() {
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpHals(int out_fd = STDOUT_FILENO) {
if (!ds.IsZipping()) {
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(),
+ false, out_fd);
return;
}
- DurationReporter duration_reporter("DUMP HALS");
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
+ false, out_fd);
using android::hidl::manager::V1_0::IServiceManager;
using android::hardware::defaultServiceManager;
@@ -1356,6 +1378,7 @@
}, '_');
const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName;
+ bool empty = false;
{
auto fd = android::base::unique_fd(
TEMP_FAILURE_RETRY(open(path.c_str(),
@@ -1370,13 +1393,14 @@
{"lshal", "debug", "-E", interface},
CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
- bool empty = 0 == lseek(fd, 0, SEEK_END);
- if (!empty) {
- ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path);
- }
+ empty = 0 == lseek(fd, 0, SEEK_END);
}
-
- unlink(path.c_str());
+ if (!empty) {
+ ds.EnqueueAddZipEntryAndCleanupIfNeeded("lshal-debug/" + cleanName + ".txt",
+ path);
+ } else {
+ unlink(path.c_str());
+ }
}
});
@@ -1464,6 +1488,73 @@
printf("========================================================\n");
}
+/*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpCheckins(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Checkins\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}, out_fd);
+ RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}, out_fd);
+ RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"}, out_fd);
+}
+
+/*
+ * Runs dumpsys on activity service to dump all application activities, services
+ * and providers in the device.
+ *
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+static void DumpAppInfos(int out_fd = STDOUT_FILENO) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Activities\n");
+ dprintf(out_fd, "========================================================\n");
+
+ // The following dumpsys internally collects output from running apps, so it can take a long
+ // time. So let's extend the timeout.
+
+ const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
+
+ RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Services (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, out_fd);
+
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Running Application Providers (non-platform)\n");
+ dprintf(out_fd, "========================================================\n");
+
+ RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd);
+}
+
// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
// 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
@@ -1471,6 +1562,19 @@
static Dumpstate::RunStatus dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
+ // Enqueue slow functions into the thread pool, if the parallel run is enabled.
+ 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);
+
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1);
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _1);
+ }
+
// Dump various things. Note that anything that takes "long" (i.e. several seconds) should
// check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped
// in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK).
@@ -1502,7 +1606,11 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
CommandOptions::AS_ROOT);
- DumpHals();
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_HALS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
RunCommand("PRINTENV", {"printenv"});
RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -1583,7 +1691,11 @@
ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_BOARD_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
/* Migrate the ril_dumpstate to a device specific dumpstate? */
int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
@@ -1605,57 +1717,17 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
- printf("========================================================\n");
- printf("== Checkins\n");
- printf("========================================================\n");
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins);
+ }
- RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
-
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"});
-
- RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
- RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
- RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
- RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
-
- printf("========================================================\n");
- printf("== Running Application Activities\n");
- printf("========================================================\n");
-
- // The following dumpsys internally collects output from running apps, so it can take a long
- // time. So let's extend the timeout.
-
- const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
-
- RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Services (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
-
- printf("========================================================\n");
- printf("== Running Application Providers (non-platform)\n");
- printf("========================================================\n");
-
- RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos);
+ }
printf("========================================================\n");
printf("== Dropbox crashes\n");
@@ -1680,7 +1752,12 @@
// Add linker configuration directory
ds.AddDir(LINKERCONFIG_DIR, true);
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
+ if (ds.dump_pool_) {
+ WAIT_TASK_WITH_CONSENT_CHECK(DUMP_INCIDENT_REPORT_TASK, ds.dump_pool_);
+ } else {
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_INCIDENT_REPORT_TASK,
+ DumpIncidentReport);
+ }
return Dumpstate::RunStatus::OK;
}
@@ -1775,31 +1852,39 @@
return status;
}
+// Common states for telephony and wifi which are needed to be collected before
+// dumpstate drop the root user.
+static void DumpstateRadioAsRoot() {
+ DumpIpTablesAsRoot();
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
// This method collects common dumpsys for telephony and wifi. Typically, wifi
// reports are fine to include all information, but telephony reports on user
// builds need to strip some content (see DumpstateTelephonyOnly).
static void DumpstateRadioCommon(bool include_sensitive_info = true) {
- DumpIpTablesAsRoot();
-
- ds.AddDir(LOGPERSIST_DATA_DIR, false);
-
- if (!DropRootUser()) {
- return;
- }
-
// We need to be picky about some stuff for telephony reports on user builds.
if (!include_sensitive_info) {
// Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
DoRadioLogcat();
} else {
+ // DumpHals takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_HALS_TASK, &DumpHals, _1);
+ }
// Contains various system properties and process startup info.
do_dmesg();
// Logs other than the radio buffer may contain package/component names and potential PII.
DoLogcat();
// Too broad for connectivity problems.
DoKmsg();
- // Contains unrelated hardware info (camera, NFC, biometrics, ...).
- DumpHals();
+ // DumpHals contains unrelated hardware info (camera, NFC, biometrics, ...).
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_HALS_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_HALS_TASK, DumpHals);
+ }
}
DumpPacketStats();
@@ -1823,6 +1908,21 @@
const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped, and two additional threads
+ // are created for DumpHals in the DumpstateRadioCommon and DumpstateBoard.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/2);
+
+ // DumpstateBoard takes long time, post it to the another thread in the pool,
+ // if pool is available.
+ ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1);
+ }
+
DumpstateRadioCommon(include_sensitive_info);
if (include_sensitive_info) {
@@ -1899,12 +1999,29 @@
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
+
+ if (ds.dump_pool_) {
+ ds.dump_pool_->waitForTask(DUMP_BOARD_TASK);
+ } else {
+ RUN_SLOW_FUNCTION_AND_LOG(DUMP_BOARD_TASK, ds.DumpstateBoard);
+ }
}
// This method collects dumpsys for wifi debugging only
static void DumpstateWifiOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+ DumpstateRadioAsRoot();
+ if (!DropRootUser()) {
+ return;
+ }
+
+ // Starts thread pool after the root user is dropped. Only one additional
+ // thread is needed for DumpHals in the DumpstateRadioCommon.
+ if (ds.dump_pool_) {
+ ds.dump_pool_->start(/*thread_counts =*/1);
+ }
+
DumpstateRadioCommon();
printf("========================================================\n");
@@ -2029,11 +2146,10 @@
return RunStatus::OK;
}
-void Dumpstate::DumpstateBoard() {
- DurationReporter duration_reporter("dumpstate_board()");
- printf("========================================================\n");
- printf("== Board\n");
- printf("========================================================\n");
+void Dumpstate::DumpstateBoard(int out_fd) {
+ dprintf(out_fd, "========================================================\n");
+ dprintf(out_fd, "== Board\n");
+ dprintf(out_fd, "========================================================\n");
if (!IsZipping()) {
MYLOGD("Not dumping board info because it's not a zipped bugreport\n");
@@ -2159,8 +2275,9 @@
MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str());
continue;
}
- AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
- printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
+ remover[i].Disable();
+ EnqueueAddZipEntryAndCleanupIfNeeded(kDumpstateBoardFiles[i], paths[i]);
+ dprintf(out_fd, "*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
}
}
@@ -2190,6 +2307,11 @@
}
bool Dumpstate::FinishZipFile() {
+ // Runs all enqueued adding zip entry and cleanup tasks before finishing the zip file.
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/* do_cancel = */false);
+ }
+
std::string entry_name = base_name_ + "-" + name_ + ".txt";
MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
tmp_path_.c_str());
@@ -2393,7 +2515,6 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
// Currently, the dumpstate binder is only used by Shell to update progress.
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
@@ -2405,7 +2526,6 @@
options->dumpstate_hal_mode = DumpstateMode::REMOTE;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
- options->do_start_service = true;
options->do_progress_updates = true;
options->do_zip_file = true;
options->do_screenshot = is_screenshot_requested;
@@ -2432,12 +2552,12 @@
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
MYLOGI(
"do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
- "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+ "is_remote_mode: %d show_header_only: %d telephony_only: %d "
"wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
"limited_only: %d args: %s\n",
options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
options.do_screenshot, options.is_remote_mode, options.show_header_only,
- options.do_start_service, options.telephony_only, options.wifi_only,
+ options.telephony_only, options.wifi_only,
options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(),
toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str());
}
@@ -2568,6 +2688,15 @@
}
tombstone_data_.clear();
anr_data_.clear();
+
+ // Instead of shutdown the pool, we delete temporary files directly since
+ // shutdown blocking the call.
+ if (dump_pool_) {
+ dump_pool_->deleteTempFiles();
+ }
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/*do_cancel =*/ true);
+ }
}
/*
@@ -2653,15 +2782,6 @@
register_sig_handler();
- // TODO(b/111441001): maybe skip if already started?
- if (options_->do_start_service) {
- MYLOGI("Starting 'dumpstate' service\n");
- android::status_t ret;
- if ((ret = android::os::DumpstateService::Start()) != android::OK) {
- MYLOGE("Unable to start DumpstateService: %d\n", ret);
- }
- }
-
if (PropertiesHelper::IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -2773,7 +2893,6 @@
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly(calling_package);
- DumpstateBoard();
} else if (options_->wifi_only) {
MaybeTakeEarlyScreenshot();
onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package);
@@ -2936,6 +3055,7 @@
return;
}
dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+ zip_entry_tasks_ = std::make_unique<TaskQueue>();
}
void Dumpstate::ShutdownDumpPool() {
@@ -2943,6 +3063,27 @@
dump_pool_->shutdown();
dump_pool_ = nullptr;
}
+ if (zip_entry_tasks_) {
+ zip_entry_tasks_->run(/* do_cancel = */true);
+ zip_entry_tasks_ = nullptr;
+ }
+}
+
+void Dumpstate::EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+ const std::string& entry_path) {
+ auto func_add_zip_entry_and_cleanup = [=](bool task_cancelled) {
+ if (!task_cancelled) {
+ AddZipEntry(entry_name, entry_path);
+ }
+ android::os::UnlinkAndLogOnError(entry_path);
+ };
+ if (zip_entry_tasks_) {
+ // Enqueues AddZipEntryAndCleanup function if the parallel run is enabled.
+ zip_entry_tasks_->add(func_add_zip_entry_and_cleanup, _1);
+ } else {
+ // Invokes AddZipEntryAndCleanup immediately
+ std::invoke(func_add_zip_entry_and_cleanup, /* task_cancelled = */false);
+ }
}
Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() {
@@ -3666,10 +3807,11 @@
}
int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
- const CommandOptions& options, bool verbose_duration) {
- DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration);
+ const CommandOptions& options, bool verbose_duration, int out_fd) {
+ DurationReporter duration_reporter(title, false /* logcat_only */,
+ verbose_duration, out_fd);
- int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options);
+ int status = RunCommandToFd(out_fd, title, full_command, options);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -3681,11 +3823,11 @@
}
void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
- const CommandOptions& options, long dumpsysTimeoutMs) {
+ const CommandOptions& options, long dumpsysTimeoutMs, int out_fd) {
long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs();
std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)};
dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
- RunCommand(title, dumpsys, options);
+ RunCommand(title, dumpsys, options, false, out_fd);
}
int open_socket(const char *service) {
@@ -3808,12 +3950,16 @@
fclose(fp);
}
-// TODO: make this function thread safe if sections are generated in parallel.
void Dumpstate::UpdateProgress(int32_t delta_sec) {
if (progress_ == nullptr) {
MYLOGE("UpdateProgress: progress_ not set\n");
return;
}
+ // This function updates progress related members of the dumpstate and reports
+ // progress percentage to the bugreport client. Since it could be called by
+ // different dump tasks at the same time if the parallel run is enabled, a
+ // mutex lock is necessary here to synchronize the call.
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
// Always update progess so stats can be tuned...
progress_->Inc(delta_sec);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index d400dc7..9582c9d 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -36,6 +36,7 @@
#include "DumpstateUtil.h"
#include "DumpPool.h"
+#include "TaskQueue.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
@@ -229,11 +230,13 @@
* |full_command| array containing the command (first entry) and its arguments.
* Must contain at least one element.
* |options| optional argument defining the command's behavior.
+ * |out_fd| A fd to support the DumpPool to output results to a temporary
+ * file. Using STDOUT_FILENO if it's not running in the parallel task.
*/
int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
const android::os::dumpstate::CommandOptions& options =
android::os::dumpstate::CommandOptions::DEFAULT,
- bool verbose_duration = false);
+ bool verbose_duration = false, int out_fd = STDOUT_FILENO);
/*
* Runs `dumpsys` with the given arguments, automatically setting its timeout
@@ -246,10 +249,12 @@
* |options| optional argument defining the command's behavior.
* |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the
* timeout from `options`)
+ * |out_fd| A fd to support the DumpPool to output results to a temporary
+ * file. Using STDOUT_FILENO if it's not running in the parallel task.
*/
void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS,
- long dumpsys_timeout_ms = 0);
+ long dumpsys_timeout_ms = 0, int out_fd = STDOUT_FILENO);
/*
* Prints the contents of a file.
@@ -306,7 +311,12 @@
// Returns OK in all other cases.
RunStatus DumpTraces(const char** path);
- void DumpstateBoard();
+ /*
+ * |out_fd| A fd to support the DumpPool to output results to a temporary file.
+ * Dumpstate can pick up later and output to the bugreport. Using STDOUT_FILENO
+ * if it's not running in the parallel task.
+ */
+ void DumpstateBoard(int out_fd = STDOUT_FILENO);
/*
* Updates the overall progress of the bugreport generation by the given weight increment.
@@ -363,6 +373,18 @@
bool CalledByApi() const;
/*
+ * Enqueues a task to the dumpstate's TaskQueue if the parallel run is enabled,
+ * otherwise invokes it immediately. The task adds file at path entry_path
+ * as a zip file entry with name entry_name. Unlinks entry_path when done.
+ *
+ * All enqueued tasks will be executed in the dumpstate's FinishZipFile method
+ * before the zip file is finished. Tasks will be cancelled in dumpstate's
+ * ShutdownDumpPool method if they have never been called.
+ */
+ void EnqueueAddZipEntryAndCleanupIfNeeded(const std::string& entry_name,
+ const std::string& entry_path);
+
+ /*
* Structure to hold options that determine the behavior of dumpstate.
*/
struct DumpOptions {
@@ -376,7 +398,6 @@
bool is_screenshot_copied = false;
bool is_remote_mode = false;
bool show_header_only = false;
- bool do_start_service = false;
bool telephony_only = false;
bool wifi_only = false;
// Trimmed-down version of dumpstate to only include whitelisted logs.
@@ -495,6 +516,10 @@
// A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
+ // A task queue to collect adding zip entry tasks inside dump tasks if the
+ // parallel run is enabled.
+ std::unique_ptr<android::os::dumpstate::TaskQueue> zip_entry_tasks_;
+
// A callback to IncidentCompanion service, which checks user consent for sharing the
// bugreport with the calling app. If the user has not responded yet to the dialog it will
// be neither confirmed nor denied.
@@ -548,6 +573,8 @@
android::sp<ConsentCallback> consent_callback_;
+ std::recursive_mutex mutex_;
+
DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index b3cb434..6b93692 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -258,7 +258,6 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
- EXPECT_FALSE(options_.do_start_service);
EXPECT_FALSE(options_.limited_only);
}
@@ -267,7 +266,6 @@
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
@@ -303,7 +301,6 @@
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_TRUE(options_.do_start_service);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
// Other options retain default values
@@ -1026,6 +1023,7 @@
SetParallelRun(true);
EnableParallelRunIfNeeded();
EXPECT_TRUE(ds.options_->OutputToFile());
+ EXPECT_TRUE(ds.zip_entry_tasks_);
EXPECT_TRUE(ds.dump_pool_);
}
@@ -1033,12 +1031,14 @@
ds.options_->use_socket = true;
EnableParallelRunIfNeeded();
EXPECT_FALSE(ds.options_->OutputToFile());
+ EXPECT_FALSE(ds.zip_entry_tasks_);
EXPECT_FALSE(ds.dump_pool_);
}
TEST_F(DumpstateTest, DumpPool_withParallelRunDisabled_isNull) {
SetParallelRun(false);
EnableParallelRunIfNeeded();
+ EXPECT_FALSE(ds.zip_entry_tasks_);
EXPECT_FALSE(ds.dump_pool_);
}
@@ -1749,6 +1749,57 @@
EXPECT_THAT(getTempFileCounts(kTestDataPath), Eq(0));
}
+class TaskQueueTest : public DumpstateBaseTest {
+public:
+ void SetUp() {
+ DumpstateBaseTest::SetUp();
+ }
+
+ TaskQueue task_queue_;
+};
+
+TEST_F(TaskQueueTest, runTask) {
+ bool is_task1_run = false;
+ bool is_task2_run = false;
+ auto task_1 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task1_run = true;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ if (task_cancelled) {
+ return;
+ }
+ is_task2_run = true;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */false);
+
+ EXPECT_TRUE(is_task1_run);
+ EXPECT_TRUE(is_task2_run);
+}
+
+TEST_F(TaskQueueTest, runTask_withCancelled) {
+ bool is_task1_cancelled = false;
+ bool is_task2_cancelled = false;
+ auto task_1 = [&](bool task_cancelled) {
+ is_task1_cancelled = task_cancelled;
+ };
+ auto task_2 = [&](bool task_cancelled) {
+ is_task2_cancelled = task_cancelled;
+ };
+ task_queue_.add(task_1, std::placeholders::_1);
+ task_queue_.add(task_2, std::placeholders::_1);
+
+ task_queue_.run(/* do_cancel = */true);
+
+ EXPECT_TRUE(is_task1_cancelled);
+ EXPECT_TRUE(is_task2_cancelled);
+}
+
} // namespace dumpstate
} // namespace os
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index 3467898..67a77f6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -55,6 +55,7 @@
MOCK_METHOD1(listServices, Vector<String16>(int));
MOCK_METHOD1(waitForService, sp<IBinder>(const String16&));
MOCK_METHOD1(isDeclared, bool(const String16&));
+ MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
};
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 17015a4..96875d5 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -20,14 +20,12 @@
"execv_helper.cpp",
"globals.cpp",
"run_dex2oat.cpp",
+ "unique_file.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
":installd_aidl",
],
- header_libs: [
- "dex2oat_headers",
- ],
shared_libs: [
"libbase",
"libbinder",
@@ -115,6 +113,7 @@
srcs: [
"run_dex2oat_test.cpp",
"run_dex2oat.cpp",
+ "unique_file.cpp",
"execv_helper.cpp",
],
cflags: ["-Wall", "-Werror"],
@@ -122,9 +121,6 @@
"libbase",
"server_configurable_flags",
],
- static_libs: [
- //"libinstalld",
- ],
test_config: "run_dex2oat_test.xml",
}
@@ -235,15 +231,13 @@
"otapreopt.cpp",
"otapreopt_utils.cpp",
"run_dex2oat.cpp",
+ "unique_file.cpp",
"utils.cpp",
"utils_default.cpp",
"view_compiler.cpp",
],
- header_libs: ["dex2oat_headers"],
-
static_libs: [
- "libartimagevalues",
"libdiskusage",
"libotapreoptparameters",
],
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index eb1bbd9..b821578 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -946,6 +946,33 @@
auto scope_guard = android::base::make_scope_guard(deleter);
+ if (storageFlags & FLAG_STORAGE_DE) {
+ auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
+ auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
+ auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
+ snapshotId, package_name);
+
+ int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
+ if (rc != 0) {
+ return error(rc, "Failed to create folder " + to);
+ }
+
+ rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
+ if (rc != 0) {
+ return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
+ }
+
+ // Check if we have data to copy.
+ if (access(from.c_str(), F_OK) == 0) {
+ rc = copy_directory_recursive(from.c_str(), to.c_str());
+ }
+ if (rc != 0) {
+ res = error(rc, "Failed copying " + from + " to " + to);
+ clear_de_on_exit = true;
+ return res;
+ }
+ }
+
// The app may not have any data at all, in which case it's OK to skip here.
auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
if (access(from_ce.c_str(), F_OK) != 0) {
@@ -971,30 +998,6 @@
LOG(WARNING) << "Failed to clear code_cache of app " << packageName;
}
- if (storageFlags & FLAG_STORAGE_DE) {
- auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
- auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
- snapshotId, package_name);
-
- int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
- if (rc != 0) {
- return error(rc, "Failed to create folder " + to);
- }
-
- rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
- if (rc != 0) {
- return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
- }
-
- rc = copy_directory_recursive(from.c_str(), to.c_str());
- if (rc != 0) {
- res = error(rc, "Failed copying " + from + " to " + to);
- clear_de_on_exit = true;
- return res;
- }
- }
-
if (storageFlags & FLAG_STORAGE_CE) {
auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 2b36067..5076ae6 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -39,7 +39,6 @@
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
-#include <dex2oat_return_codes.h>
#include <log/log.h> // TODO: Move everything to base/logging.
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
@@ -55,6 +54,7 @@
#include "installd_deps.h"
#include "otapreopt_utils.h"
#include "run_dex2oat.h"
+#include "unique_file.h"
#include "utils.h"
using android::base::Basename;
@@ -231,6 +231,12 @@
return profile_boot_class_path == "true";
}
+static void UnlinkIgnoreResult(const std::string& path) {
+ if (unlink(path.c_str()) < 0) {
+ PLOG(ERROR) << "Failed to unlink " << path;
+ }
+}
+
/*
* Whether dexopt should use a swap file when compiling an APK.
*
@@ -347,6 +353,16 @@
return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
}
+static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
+ const std::string& location, bool read_write, bool is_secondary_dex) {
+ std::string profile_path = create_reference_profile_path(package_name, location,
+ is_secondary_dex);
+ unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+ return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
+ clear_profile(path);
+ });
+}
+
static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
const std::string& location) {
std::string profile = create_snapshot_profile_path(package_name, location);
@@ -837,118 +853,14 @@
return true;
}
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-// Dex2oatFileWrapper file(open(...),
-// [name]() {
-// unlink(name.c_str());
-// });
-// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-// wrapper if captured as a reference.
-//
-// if (file.get() == -1) {
-// // Error opening...
-// }
-//
-// ...
-// if (error) {
-// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-// // and delete the file (after the fd is closed).
-// return -1;
-// }
-//
-// (Success case)
-// file.SetCleanup(false);
-// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
-// // (leaving the file around; after the fd is closed).
-//
-class Dex2oatFileWrapper {
- public:
- Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) {
- }
-
- Dex2oatFileWrapper(int value, std::function<void ()> cleanup)
- : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
-
- Dex2oatFileWrapper(Dex2oatFileWrapper&& other) {
- value_ = other.value_;
- cleanup_ = other.cleanup_;
- do_cleanup_ = other.do_cleanup_;
- auto_close_ = other.auto_close_;
- other.release();
- }
-
- Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) {
- value_ = other.value_;
- cleanup_ = other.cleanup_;
- do_cleanup_ = other.do_cleanup_;
- auto_close_ = other.auto_close_;
- other.release();
- return *this;
- }
-
- ~Dex2oatFileWrapper() {
- reset(-1);
- }
-
- int get() {
- return value_;
- }
-
- void SetCleanup(bool cleanup) {
- do_cleanup_ = cleanup;
- }
-
- void reset(int new_value) {
- if (auto_close_ && value_ >= 0) {
- close(value_);
- }
- if (do_cleanup_ && cleanup_ != nullptr) {
- cleanup_();
- }
-
- value_ = new_value;
- }
-
- void reset(int new_value, std::function<void ()> new_cleanup) {
- if (auto_close_ && value_ >= 0) {
- close(value_);
- }
- if (do_cleanup_ && cleanup_ != nullptr) {
- cleanup_();
- }
-
- value_ = new_value;
- cleanup_ = new_cleanup;
- }
-
- void DisableAutoClose() {
- auto_close_ = false;
- }
-
- private:
- void release() {
- value_ = -1;
- do_cleanup_ = false;
- cleanup_ = nullptr;
- }
- int value_;
- std::function<void ()> cleanup_;
- bool do_cleanup_;
- bool auto_close_;
-};
-
// (re)Creates the app image if needed.
-Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
+UniqueFile maybe_open_app_image(const std::string& out_oat_path,
bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// In case there is a stale image, remove it now. Ignore any error.
@@ -956,18 +868,19 @@
// Not enabled, exit.
if (!generate_app_image) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
if (app_image_format.empty()) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// Recreate is true since we do not want to modify a mapped image. If the app is
// already running and we modify the image file, it can cause crashes (b/27493510).
- Dex2oatFileWrapper wrapper_fd(
+ UniqueFile image_file(
open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
- [image_path]() { unlink(image_path.c_str()); });
- if (wrapper_fd.get() < 0) {
+ image_path,
+ UnlinkIgnoreResult);
+ if (image_file.fd() < 0) {
// Could not create application image file. Go on since we can compile without it.
LOG(ERROR) << "installd could not create '" << image_path
<< "' for image file during dexopt";
@@ -978,21 +891,21 @@
}
}
} else if (!set_permissions_and_ownership(
- wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
+ image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
- wrapper_fd.reset(-1);
+ image_file.reset();
}
- return wrapper_fd;
+ return image_file;
}
// Creates the dexopt swap file if necessary and return its fd.
// Returns -1 if there's no need for a swap or in case of errors.
-unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+unique_fd maybe_open_dexopt_swap_file(const std::string& out_oat_path) {
if (!ShouldUseSwapFileForDexopt()) {
return invalid_unique_fd();
}
- auto swap_file_name = std::string(out_oat_path) + ".swap";
+ auto swap_file_name = out_oat_path + ".swap";
unique_fd swap_fd(open_output_file(
swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600));
if (swap_fd.get() < 0) {
@@ -1010,13 +923,13 @@
// Opens the reference profiles if needed.
// Note that the reference profile might not exist so it's OK if the fd will be -1.
-Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname,
+UniqueFile maybe_open_reference_profile(const std::string& pkgname,
const std::string& dex_path, const char* profile_name, bool profile_guided,
bool is_public, int uid, bool is_secondary_dex) {
// If we are not profile guided compilation, or we are compiling system server
// do not bother to open the profiles; we won't be using them.
if (!profile_guided || (pkgname[0] == '*')) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// If this is a secondary dex path which is public do not open the profile.
@@ -1028,7 +941,7 @@
// compiling with a public profile from the .dm file the PackageManager will
// set is_public toghether with the profile guided compilation.
if (is_secondary_dex && is_public) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
// Open reference profile in read only mode as dex2oat does not get write permissions.
@@ -1038,33 +951,28 @@
} else {
if (profile_name == nullptr) {
// This path is taken for system server re-compilation lunched from ZygoteInit.
- return Dex2oatFileWrapper();
+ return UniqueFile();
} else {
location = profile_name;
}
}
- unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false,
- is_secondary_dex);
- const auto& cleanup = [pkgname, location, is_secondary_dex]() {
- clear_reference_profile(pkgname, location, is_secondary_dex);
- };
- return Dex2oatFileWrapper(ufd.release(), cleanup);
+ return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
+ is_secondary_dex);
}
-// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
-// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
+// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
+// out_vdex_wrapper. Returns true for success or false in case of errors.
bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
- bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
- Dex2oatFileWrapper* out_vdex_wrapper_fd) {
- CHECK(in_vdex_wrapper_fd != nullptr);
- CHECK(out_vdex_wrapper_fd != nullptr);
+ bool profile_guided, UniqueFile* in_vdex_wrapper,
+ UniqueFile* out_vdex_wrapper) {
+ CHECK(in_vdex_wrapper != nullptr);
+ CHECK(out_vdex_wrapper != nullptr);
// Open the existing VDEX. We do this before creating the new output VDEX, which will
// unlink the old one.
char in_odex_path[PKG_PATH_MAX];
int dexopt_action = abs(dexopt_needed);
bool is_odex_location = dexopt_needed < 0;
- std::string in_vdex_path_str;
// Infer the name of the output VDEX.
const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
@@ -1086,7 +994,7 @@
} else {
path = out_oat_path;
}
- in_vdex_path_str = create_vdex_filename(path);
+ std::string in_vdex_path_str = create_vdex_filename(path);
if (in_vdex_path_str.empty()) {
ALOGE("installd cannot compute input vdex location for '%s'\n", path);
return false;
@@ -1104,13 +1012,15 @@
!profile_guided;
if (update_vdex_in_place) {
// Open the file read-write to be able to update it.
- in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
- if (in_vdex_wrapper_fd->get() == -1) {
+ in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
+ in_vdex_path_str);
+ if (in_vdex_wrapper->fd() == -1) {
// If we failed to open the file, we cannot update it in place.
update_vdex_in_place = false;
}
} else {
- in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+ in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
+ in_vdex_path_str);
}
}
@@ -1119,22 +1029,24 @@
if (update_vdex_in_place) {
// We unlink the file in case the invocation of dex2oat fails, to ensure we don't
// have bogus stale vdex files.
- out_vdex_wrapper_fd->reset(
- in_vdex_wrapper_fd->get(),
- [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+ out_vdex_wrapper->reset(
+ in_vdex_wrapper->fd(),
+ out_vdex_path_str,
+ UnlinkIgnoreResult);
// Disable auto close for the in wrapper fd (it will be done when destructing the out
// wrapper).
- in_vdex_wrapper_fd->DisableAutoClose();
+ in_vdex_wrapper->DisableAutoClose();
} else {
- out_vdex_wrapper_fd->reset(
+ out_vdex_wrapper->reset(
open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
- [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
- if (out_vdex_wrapper_fd->get() < 0) {
+ out_vdex_path_str,
+ UnlinkIgnoreResult);
+ if (out_vdex_wrapper->fd() < 0) {
ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
return false;
}
}
- if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
+ if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
out_vdex_path_str.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
return false;
@@ -1145,25 +1057,24 @@
}
// Opens the output oat file for the given apk.
-// If successful it stores the output path into out_oat_path and returns true.
-Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
- bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
- char* out_oat_path) {
+UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
+ bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+ char out_oat_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
- return Dex2oatFileWrapper();
+ return UniqueFile();
}
- const std::string out_oat_path_str(out_oat_path);
- Dex2oatFileWrapper wrapper_fd(
+ UniqueFile oat(
open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
- [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
- if (wrapper_fd.get() < 0) {
+ out_oat_path,
+ UnlinkIgnoreResult);
+ if (oat.fd() < 0) {
PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path;
} else if (!set_permissions_and_ownership(
- wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) {
+ oat.fd(), is_public, uid, out_oat_path, is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
- wrapper_fd.reset(-1);
+ oat.reset();
}
- return wrapper_fd;
+ return oat;
}
// Creates RDONLY fds for oat and vdex files, if exist.
@@ -1770,8 +1681,8 @@
}
// Open the input file.
- unique_fd input_fd(open(dex_path, O_RDONLY, 0));
- if (input_fd.get() < 0) {
+ UniqueFile in_dex(open(dex_path, O_RDONLY, 0), dex_path);
+ if (in_dex.fd() < 0) {
*error_msg = StringPrintf("installd cannot open '%s' for input during dexopt", dex_path);
LOG(ERROR) << *error_msg;
return -1;
@@ -1785,19 +1696,19 @@
}
// Create the output OAT file.
- char out_oat_path[PKG_PATH_MAX];
- Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
- instruction_set, is_secondary_dex, out_oat_path);
- if (out_oat_fd.get() < 0) {
+ UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+ instruction_set, is_secondary_dex);
+ if (out_oat.fd() < 0) {
*error_msg = "Could not open out oat file.";
return -1;
}
// Open vdex files.
- Dex2oatFileWrapper in_vdex_fd;
- Dex2oatFileWrapper out_vdex_fd;
- if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set,
- is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+ UniqueFile in_vdex;
+ UniqueFile out_vdex;
+ if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
+ instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
+ &out_vdex)) {
*error_msg = "Could not open vdex files.";
return -1;
}
@@ -1817,26 +1728,27 @@
}
// Create a swap file if necessary.
- unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+ unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat.path());
// Open the reference profile if needed.
- Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
+ UniqueFile reference_profile = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
- if (reference_profile_fd.get() == -1) {
+ if (reference_profile.fd() == -1) {
// We don't create an app image without reference profile since there is no speedup from
// loading it in that case and instead will be a small overhead.
generate_app_image = false;
}
// Create the app image file if needed.
- Dex2oatFileWrapper image_fd = maybe_open_app_image(
- out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);
+ UniqueFile out_image = maybe_open_app_image(
+ out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
- unique_fd dex_metadata_fd;
+ UniqueFile dex_metadata;
if (dex_metadata_path != nullptr) {
- dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
- if (dex_metadata_fd.get() < 0) {
+ dex_metadata.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)),
+ dex_metadata_path);
+ if (dex_metadata.fd() < 0) {
PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path;
}
}
@@ -1863,26 +1775,24 @@
LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
RunDex2Oat runner(dex2oat_bin, execv_helper.get());
- runner.Initialize(input_fd.get(),
- out_oat_fd.get(),
- in_vdex_fd.get(),
- out_vdex_fd.get(),
- image_fd.get(),
- dex_path,
- out_oat_path,
+ runner.Initialize(out_oat,
+ out_vdex,
+ out_image,
+ in_dex,
+ in_vdex,
+ dex_metadata,
+ reference_profile,
+ class_loader_context,
+ join_fds(context_input_fds),
swap_fd.get(),
instruction_set,
compiler_filter,
debuggable,
boot_complete,
for_restore,
- reference_profile_fd.get(),
- class_loader_context,
- join_fds(context_input_fds),
target_sdk_version,
enable_hidden_api_checks,
generate_compact_dex,
- dex_metadata_fd.get(),
use_jitzygote_image,
compilation_reason);
@@ -1892,8 +1802,8 @@
drop_capabilities(uid);
SetDex2OatScheduling(boot_complete);
- if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
- PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
+ if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
+ PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
_exit(DexoptReturnCodes::kFlock);
}
@@ -1910,13 +1820,13 @@
}
}
- update_out_oat_access_times(dex_path, out_oat_path);
+ update_out_oat_access_times(dex_path, out_oat.path().c_str());
// We've been successful, don't delete output.
- out_oat_fd.SetCleanup(false);
- out_vdex_fd.SetCleanup(false);
- image_fd.SetCleanup(false);
- reference_profile_fd.SetCleanup(false);
+ out_oat.DisableCleanup();
+ out_vdex.DisableCleanup();
+ out_image.DisableCleanup();
+ reference_profile.DisableCleanup();
return 0;
}
diff --git a/cmds/installd/dexopt_return_codes.h b/cmds/installd/dexopt_return_codes.h
index bbecfa4..e5198ad 100644
--- a/cmds/installd/dexopt_return_codes.h
+++ b/cmds/installd/dexopt_return_codes.h
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <dex2oat_return_codes.h>
-
namespace android {
namespace installd {
@@ -70,48 +68,21 @@
return nullptr;
}
-inline const char* get_dex2oat_return_code_name(art::dex2oat::ReturnCode code) {
- switch (code) {
- case art::dex2oat::ReturnCode::kNoFailure:
- return "dex2oat success";
- case art::dex2oat::ReturnCode::kOther:
- return "unspecified dex2oat error";
- case art::dex2oat::ReturnCode::kCreateRuntime:
- return "dex2oat failed to create a runtime";
+inline const char* get_dex2oat_return_code_name(int code) {
+ if (code == 0) {
+ return "dex2oat success";
+ } else {
+ return "dex2oat error";
}
- return nullptr;
}
-// Get some slightly descriptive string for the return code. Handles both DexoptReturnCodes (local
-// exit codes) as well as art::dex2oat::ReturnCode.
+// Get some slightly descriptive string for the return code.
inline const char* get_return_code_name(int code) {
- // Try to enforce non-overlap (see comment on DexoptReturnCodes)
- // TODO: How could switch-case checks be used to enforce completeness?
- switch (code) {
- case kSetGid:
- case kSetUid:
- case kCapSet:
- case kFlock:
- case kProfmanExec:
- case kSetSchedPolicy:
- case kSetPriority:
- case kDex2oatExec:
- case kInstructionSetLength:
- case kHashValidatePath:
- case kHashOpenPath:
- case kHashReadDex:
- case kHashWrite:
- break;
- case static_cast<int>(art::dex2oat::ReturnCode::kNoFailure):
- case static_cast<int>(art::dex2oat::ReturnCode::kOther):
- case static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime):
- break;
- }
const char* value = get_installd_return_code_name(static_cast<DexoptReturnCodes>(code));
if (value != nullptr) {
return value;
}
- value = get_dex2oat_return_code_name(static_cast<art::dex2oat::ReturnCode>(code));
+ value = get_dex2oat_return_code_name(code);
return value;
}
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 9c75781..443821c 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -31,10 +31,8 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <art_image_values.h>
#include <cutils/fs.h>
#include <cutils/properties.h>
-#include <dex2oat_return_codes.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 8cac58f..17ea903 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -30,6 +30,8 @@
#include <log/log.h>
#include <server_configurable_flags/get_flags.h>
+#include "unique_file.h"
+
using android::base::Basename;
using android::base::StringPrintf;
@@ -64,263 +66,42 @@
RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper)
: dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {}
-void RunDex2Oat::Initialize(int zip_fd,
- int oat_fd,
- int input_vdex_fd,
- int output_vdex_fd,
- int image_fd,
- const char* input_file_name,
- const char* output_file_name,
+void RunDex2Oat::Initialize(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds,
int swap_fd,
const char* instruction_set,
const char* compiler_filter,
bool debuggable,
bool post_bootcomplete,
bool for_restore,
- int profile_fd,
- const char* class_loader_context,
- const std::string& class_loader_context_fds,
int target_sdk_version,
bool enable_hidden_api_checks,
bool generate_compact_dex,
- int dex_metadata_fd,
bool use_jitzygote_image,
const char* compilation_reason) {
- // Get the relative path to the input file.
- std::string input_basename = Basename(input_file_name);
+ PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
- std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s");
- std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s");
+ PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
+ dex_metadata, profile, swap_fd, class_loader_context,
+ class_loader_context_fds);
- std::string threads_format = "-j%s";
- std::string dex2oat_threads_arg = post_bootcomplete
- ? (for_restore
- ? MapPropertyToArgWithBackup(
- "dalvik.vm.restore-dex2oat-threads",
- "dalvik.vm.dex2oat-threads",
- threads_format)
- : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format))
- : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format);
- std::string cpu_set_format = "--cpu-set=%s";
- std::string dex2oat_cpu_set_arg = post_bootcomplete
- ? (for_restore
- ? MapPropertyToArgWithBackup(
- "dalvik.vm.restore-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);
+ PrepareCompilerConfigFlags(input_vdex, output_vdex, instruction_set, compiler_filter,
+ debuggable, target_sdk_version, enable_hidden_api_checks,
+ generate_compact_dex, compilation_reason);
- std::string bootclasspath;
- char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
- if (dex2oat_bootclasspath != nullptr) {
- bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);
- }
- // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
- // BOOTCLASSPATH.
-
- const std::string dex2oat_isa_features_key =
- StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
- std::string instruction_set_features_arg =
- MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
-
- const std::string dex2oat_isa_variant_key =
- StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
- std::string instruction_set_variant_arg =
- MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
-
- const char* dex2oat_norelocation = "-Xnorelocate";
+ PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore);
const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", "");
std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags);
ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str());
- // If we are booting without the real /data, don't spend time compiling.
- std::string vold_decrypt = GetProperty("vold.decrypt", "");
- bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
- vold_decrypt == "1";
-
- std::string updatable_bcp_packages =
- MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
- "--updatable-bcp-packages-file=%s");
- if (updatable_bcp_packages.empty()) {
- // Make dex2oat fail by providing non-existent file name.
- updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
- }
-
- std::string resolve_startup_string_arg =
- MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings",
- "--resolve-startup-const-strings=%s");
- if (resolve_startup_string_arg.empty()) {
- // If empty, fall back to system property.
- resolve_startup_string_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
- "--resolve-startup-const-strings=%s");
- }
-
- const std::string image_block_size_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
- "--max-image-block-size=%s");
-
- const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
-
- std::string image_format_arg;
- if (image_fd >= 0) {
- image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s");
- }
-
- std::string dex2oat_large_app_threshold_arg =
- MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
-
- bool generate_minidebug_info = kEnableMinidebugInfo &&
- GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
-
- std::string boot_image;
- if (use_jitzygote_image) {
- boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
- } else {
- boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
- }
-
- // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
- // use arraysize instead.
- std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd);
- std::string zip_location_arg = StringPrintf("--zip-location=%s", input_basename.c_str());
- std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd);
- std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd);
- std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd);
- std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name);
- std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set);
- std::string dex2oat_compiler_filter_arg;
- std::string dex2oat_swap_fd;
- std::string dex2oat_image_fd;
- std::string target_sdk_version_arg;
- if (target_sdk_version != 0) {
- target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
- }
- std::string class_loader_context_arg;
- std::string class_loader_context_fds_arg;
- if (class_loader_context != nullptr) {
- class_loader_context_arg = StringPrintf("--class-loader-context=%s",
- class_loader_context);
- if (!class_loader_context_fds.empty()) {
- class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",
- class_loader_context_fds.c_str());
- }
- }
-
- if (swap_fd >= 0) {
- dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd);
- }
- if (image_fd >= 0) {
- dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd);
- }
-
- // Compute compiler filter.
- bool have_dex2oat_relocation_skip_flag = false;
- if (skip_compilation) {
- dex2oat_compiler_filter_arg = "--compiler-filter=extract";
- have_dex2oat_relocation_skip_flag = true;
- } else if (compiler_filter != nullptr) {
- dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter);
- }
-
- if (dex2oat_compiler_filter_arg.empty()) {
- dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
- "--compiler-filter=%s");
- }
-
- // Check whether all apps should be compiled debuggable.
- if (!debuggable) {
- debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
- }
- std::string profile_arg;
- if (profile_fd != -1) {
- profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
- }
-
- // Get the directory of the apk to pass as a base classpath directory.
- std::string base_dir;
- std::string apk_dir(input_file_name);
- unsigned long dir_index = apk_dir.rfind('/');
- bool has_base_dir = dir_index != std::string::npos;
- if (has_base_dir) {
- apk_dir = apk_dir.substr(0, dir_index);
- base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str());
- }
-
- std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
-
- std::string compilation_reason_arg = compilation_reason == nullptr
- ? ""
- : std::string("--compilation-reason=") + compilation_reason;
-
- ALOGV("Running %s in=%s out=%s\n", dex2oat_bin_.c_str(), input_basename.c_str(),
- output_file_name);
-
- // Disable cdex if update input vdex is true since this combination of options is not
- // supported.
- const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
-
- AddArg(zip_fd_arg);
- AddArg(zip_location_arg);
- AddArg(input_vdex_fd_arg);
- AddArg(output_vdex_fd_arg);
- AddArg(oat_fd_arg);
- AddArg(oat_location_arg);
- AddArg(instruction_set_arg);
-
- AddArg(instruction_set_variant_arg);
- AddArg(instruction_set_features_arg);
-
- AddArg(boot_image);
-
- AddRuntimeArg(bootclasspath);
- AddRuntimeArg(dex2oat_Xms_arg);
- AddRuntimeArg(dex2oat_Xmx_arg);
-
- AddArg(updatable_bcp_packages);
- AddArg(resolve_startup_string_arg);
- AddArg(image_block_size_arg);
- AddArg(dex2oat_compiler_filter_arg);
- AddArg(dex2oat_threads_arg);
- AddArg(dex2oat_cpu_set_arg);
- AddArg(dex2oat_swap_fd);
- AddArg(dex2oat_image_fd);
-
- if (generate_debug_info) {
- AddArg("--generate-debug-info");
- }
- if (debuggable) {
- AddArg("--debuggable");
- }
- AddArg(image_format_arg);
- AddArg(dex2oat_large_app_threshold_arg);
-
- if (have_dex2oat_relocation_skip_flag) {
- AddRuntimeArg(dex2oat_norelocation);
- }
- AddArg(profile_arg);
- AddArg(base_dir);
- AddArg(class_loader_context_arg);
- AddArg(class_loader_context_fds_arg);
- if (generate_minidebug_info) {
- AddArg(kMinidebugDex2oatFlag);
- }
- if (disable_cdex) {
- AddArg(kDisableCompactDexFlag);
- }
- AddRuntimeArg(target_sdk_version_arg);
- if (enable_hidden_api_checks) {
- AddRuntimeArg("-Xhidden-api-policy:enabled");
- }
-
- if (dex_metadata_fd > -1) {
- AddArg(dex_metadata_fd_arg);
- }
-
- AddArg(compilation_reason_arg);
-
// Do not add args after dex2oat_flags, they should override others for debugging.
for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) {
AddArg(*it);
@@ -331,8 +112,238 @@
RunDex2Oat::~RunDex2Oat() {}
+void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+ std::string boot_image;
+ if (use_jitzygote_image) {
+ boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
+ } else {
+ boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
+ }
+ AddArg(boot_image);
+
+ // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
+ // BOOTCLASSPATH.
+ char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
+ if (dex2oat_bootclasspath != nullptr) {
+ AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
+ }
+
+ std::string updatable_bcp_packages =
+ MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
+ "--updatable-bcp-packages-file=%s");
+ if (updatable_bcp_packages.empty()) {
+ // Make dex2oat fail by providing non-existent file name.
+ updatable_bcp_packages =
+ "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
+ }
+ AddArg(updatable_bcp_packages);
+}
+
+void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ int swap_fd,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds) {
+ std::string input_basename = Basename(input_dex.path());
+ LOG(VERBOSE) << "Running " << dex2oat_bin_ << " in=" << input_basename << " out="
+ << output_oat.path();
+
+ AddArg(StringPrintf("--zip-fd=%d", input_dex.fd()));
+ AddArg(StringPrintf("--zip-location=%s", input_basename.c_str()));
+ AddArg(StringPrintf("--oat-fd=%d", output_oat.fd()));
+ AddArg(StringPrintf("--oat-location=%s", output_oat.path().c_str()));
+ AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex.fd()));
+ AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex.fd()));
+
+ if (output_image.fd() >= 0) {
+ AddArg(StringPrintf("--app-image-fd=%d", output_image.fd()));
+ AddArg(MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"));
+ }
+ if (dex_metadata.fd() > -1) {
+ AddArg("--dm-fd=" + std::to_string(dex_metadata.fd()));
+ }
+ if (profile.fd() != -1) {
+ AddArg(StringPrintf("--profile-file-fd=%d", profile.fd()));
+ }
+ if (swap_fd >= 0) {
+ AddArg(StringPrintf("--swap-fd=%d", swap_fd));
+ }
+
+ // Get the directory of the apk to pass as a base classpath directory.
+ {
+ std::string apk_dir(input_dex.path());
+ size_t dir_index = apk_dir.rfind('/');
+ if (dir_index != std::string::npos) {
+ apk_dir = apk_dir.substr(0, dir_index);
+ AddArg(StringPrintf("--classpath-dir=%s", apk_dir.c_str()));
+ }
+ }
+
+ if (class_loader_context != nullptr) {
+ AddArg(StringPrintf("--class-loader-context=%s", class_loader_context));
+ if (!class_loader_context_fds.empty()) {
+ AddArg(StringPrintf("--class-loader-context-fds=%s",
+ class_loader_context_fds.c_str()));
+ }
+ }
+}
+
+void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+ const UniqueFile& output_vdex,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ const char* compilation_reason) {
+ // Disable cdex if update input vdex is true since this combination of options is not
+ // supported.
+ const bool disable_cdex = !generate_compact_dex || (input_vdex.fd() == output_vdex.fd());
+ if (disable_cdex) {
+ AddArg(kDisableCompactDexFlag);
+ }
+
+ // ISA related
+ {
+ AddArg(StringPrintf("--instruction-set=%s", instruction_set));
+
+ const std::string dex2oat_isa_features_key =
+ StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
+ std::string instruction_set_features_arg =
+ MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s");
+ AddArg(instruction_set_features_arg);
+
+ const std::string dex2oat_isa_variant_key =
+ StringPrintf("dalvik.vm.isa.%s.variant", instruction_set);
+ std::string instruction_set_variant_arg =
+ MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s");
+ AddArg(instruction_set_variant_arg);
+ }
+
+ // Compute compiler filter.
+ {
+ std::string dex2oat_compiler_filter_arg;
+ {
+ // If we are booting without the real /data, don't spend time compiling.
+ std::string vold_decrypt = GetProperty("vold.decrypt", "");
+ bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
+ vold_decrypt == "1";
+
+ bool have_dex2oat_relocation_skip_flag = false;
+ if (skip_compilation) {
+ dex2oat_compiler_filter_arg = "--compiler-filter=extract";
+ have_dex2oat_relocation_skip_flag = true;
+ } else if (compiler_filter != nullptr) {
+ dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s",
+ compiler_filter);
+ }
+ if (have_dex2oat_relocation_skip_flag) {
+ AddRuntimeArg("-Xnorelocate");
+ }
+ }
+
+ if (dex2oat_compiler_filter_arg.empty()) {
+ dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
+ "--compiler-filter=%s");
+ }
+ AddArg(dex2oat_compiler_filter_arg);
+
+ if (compilation_reason != nullptr) {
+ AddArg(std::string("--compilation-reason=") + compilation_reason);
+ }
+ }
+
+ AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
+ "--max-image-block-size=%s"));
+
+ AddArg(MapPropertyToArg("dalvik.vm.dex2oat-very-large",
+ "--very-large-app-threshold=%s"));
+
+ std::string resolve_startup_string_arg = MapPropertyToArg(
+ "persist.device_config.runtime.dex2oat_resolve_startup_strings",
+ "--resolve-startup-const-strings=%s");
+ if (resolve_startup_string_arg.empty()) {
+ // If empty, fall back to system property.
+ resolve_startup_string_arg =
+ MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
+ "--resolve-startup-const-strings=%s");
+ }
+ AddArg(resolve_startup_string_arg);
+
+ // Debug related
+ {
+ // Check whether all apps should be compiled debuggable.
+ if (!debuggable) {
+ debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1";
+ }
+ if (debuggable) {
+ AddArg("--debuggable");
+ }
+
+ const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
+ if (generate_debug_info) {
+ AddArg("--generate-debug-info");
+ }
+ {
+ bool generate_minidebug_info = kEnableMinidebugInfo &&
+ GetBoolProperty(kMinidebugInfoSystemProperty,
+ kMinidebugInfoSystemPropertyDefault);
+ if (generate_minidebug_info) {
+ AddArg(kMinidebugDex2oatFlag);
+ }
+ }
+ }
+
+ if (target_sdk_version != 0) {
+ AddRuntimeArg(StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version));
+ }
+
+ if (enable_hidden_api_checks) {
+ AddRuntimeArg("-Xhidden-api-policy:enabled");
+ }
+}
+
+void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete,
+ bool for_restore) {
+ // CPU set
+ {
+ std::string cpu_set_format = "--cpu-set=%s";
+ std::string dex2oat_cpu_set_arg = post_bootcomplete
+ ? (for_restore
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.restore-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);
+ }
+
+ // Number of threads
+ {
+ std::string threads_format = "-j%s";
+ std::string dex2oat_threads_arg = post_bootcomplete
+ ? (for_restore
+ ? MapPropertyToArgWithBackup(
+ "dalvik.vm.restore-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);
+ }
+
+ AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"));
+ AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"));
+}
+
void RunDex2Oat::Exec(int exit_code) {
- LOG(ERROR) << "RunDex2Oat::Exec";
execv_helper_->Exec(exit_code);
}
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index e4450b0..325a3a2 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -25,37 +25,59 @@
namespace android {
namespace installd {
+class UniqueFile;
+
class RunDex2Oat {
public:
explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper);
virtual ~RunDex2Oat();
- void Initialize(int zip_fd,
- int oat_fd,
- int input_vdex_fd,
- int output_vdex_fd,
- int image_fd,
- const char* input_file_name,
- const char* output_file_name,
+ void Initialize(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds,
int swap_fd,
const char* instruction_set,
const char* compiler_filter,
bool debuggable,
bool post_bootcomplete,
bool for_restore,
- int profile_fd,
- const char* class_loader_context,
- const std::string& class_loader_context_fds,
int target_sdk_version,
bool enable_hidden_api_checks,
bool generate_compact_dex,
- int dex_metadata_fd,
bool use_jitzygote_image,
const char* compilation_reason);
void Exec(int exit_code);
protected:
+ void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+ void PrepareInputFileFlags(const UniqueFile& output_oat,
+ const UniqueFile& output_vdex,
+ const UniqueFile& output_image,
+ const UniqueFile& input_dex,
+ const UniqueFile& input_vdex,
+ const UniqueFile& dex_metadata,
+ const UniqueFile& profile,
+ int swap_fd,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds);
+ void PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+ const UniqueFile& output_vdex,
+ const char* instruction_set,
+ const char* compiler_filter,
+ bool debuggable,
+ int target_sdk_version,
+ bool enable_hidden_api_checks,
+ bool generate_compact_dex,
+ const char* compilation_reason);
+ void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore);
+
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 b1f429d..3813cf7 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -24,6 +24,7 @@
#include "execv_helper.h"
#include "run_dex2oat.h"
+#include "unique_file.h"
namespace android {
namespace installd {
@@ -34,14 +35,16 @@
static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat";
static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}";
- static constexpr int ZIP_FD = 1;
- static constexpr int OAT_FD = 2;
- static constexpr int INPUT_VDEX_FD = 3;
- static constexpr int OUTPUT_VDEX_FD = 4;
- static constexpr int IMAGE_FD = 5;
- static constexpr int PROFILE_FD = 6;
- static constexpr int DEX_METADATA_FD = 7;
- static constexpr int SWAP_FD = 8;
+ // UniqueFile closes FD. Avoid using standard I/O since the test is expected to print gtest
+ // results. Alternatively, mock out UniqueFile to avoid the side effect of close(2).
+ static constexpr int ZIP_FD = 3;
+ static constexpr int OAT_FD = 4;
+ static constexpr int INPUT_VDEX_FD = 5;
+ static constexpr int OUTPUT_VDEX_FD = 6;
+ static constexpr int IMAGE_FD = 7;
+ static constexpr int PROFILE_FD = 8;
+ static constexpr int DEX_METADATA_FD = 9;
+ static constexpr int SWAP_FD = 10;
using FakeSystemProperties = std::map<std::string, std::string>;
@@ -84,37 +87,33 @@
struct RunDex2OatArgs {
static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() {
auto args = std::make_unique<RunDex2OatArgs>();
- args->input_file_name = INPUT_PATH;
- args->zip_fd = ZIP_FD;
- args->output_file_name = OUTPUT_PATH;
- args->oat_fd = OAT_FD;
- args->input_vdex_fd = INPUT_VDEX_FD;
- args->output_vdex_fd = OUTPUT_VDEX_FD;
+ args->input_dex.reset(ZIP_FD, INPUT_PATH);
+ args->output_oat.reset(OAT_FD, OUTPUT_PATH);
+ args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+ args->output_vdex.reset(OUTPUT_VDEX_FD, "UNUSED_PATH");
args->instruction_set = "arm64";
args->compilation_reason = "rundex2oattest";
return args;
}
- int zip_fd = -1;
- int oat_fd = -1;
- int input_vdex_fd = -1;
- int output_vdex_fd = -1;
- int image_fd = -1;
- const char* input_file_name = nullptr;
- const char* output_file_name = nullptr;
+ UniqueFile output_oat;
+ UniqueFile output_vdex;
+ UniqueFile output_image;
+ UniqueFile input_dex;
+ UniqueFile input_vdex;
+ UniqueFile dex_metadata;
+ UniqueFile profile;
int swap_fd = -1;
const char* instruction_set = nullptr;
const char* compiler_filter = "extract";
bool debuggable = false;
bool post_bootcomplete = false;
bool for_restore = false;
- int profile_fd = -1;
const char* class_loader_context = nullptr;
std::string class_loader_context_fds;
int target_sdk_version = 0;
bool enable_hidden_api_checks = false;
bool generate_compact_dex = true;
- int dex_metadata_fd = -1;
bool use_jitzygote_image = false;
const char* compilation_reason = nullptr;
};
@@ -143,7 +142,7 @@
cmd += arg;
cmd += " ";
}
- LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd;
+ LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd << "\n";
}
};
@@ -230,7 +229,7 @@
<< "Flag " << flag << " should be specified without value, but got " << value;
} else {
EXPECT_TRUE(execv_helper_->HasArg(flag + value))
- << "Flag " << flag << "=" << value << " is not specificed";
+ << "Flag " << flag << value << " is not specificed";
}
}
}
@@ -241,26 +240,24 @@
void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) {
FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_);
- runner.Initialize(args->zip_fd,
- args->oat_fd,
- args->input_vdex_fd,
- args->output_vdex_fd,
- args->image_fd,
- args->input_file_name,
- args->output_file_name,
+ runner.Initialize(args->output_oat,
+ args->output_vdex,
+ args->output_image,
+ args->input_dex,
+ args->input_vdex,
+ args->dex_metadata,
+ args->profile,
+ args->class_loader_context,
+ args->class_loader_context_fds,
args->swap_fd,
args->instruction_set,
args->compiler_filter,
args->debuggable,
args->post_bootcomplete,
args->for_restore,
- args->profile_fd,
- args->class_loader_context,
- args->class_loader_context_fds,
args->target_sdk_version,
args->enable_hidden_api_checks,
args->generate_compact_dex,
- args->dex_metadata_fd,
args->use_jitzygote_image,
args->compilation_reason);
runner.Exec(/*exit_code=*/ 0);
@@ -281,8 +278,8 @@
TEST_F(RunDex2OatTest, WithAllOtherInputFds) {
auto args = RunDex2OatArgs::MakeDefaultTestArgs();
- args->image_fd = IMAGE_FD;
- args->profile_fd = PROFILE_FD;
+ args->output_image.reset(IMAGE_FD, "UNUSED_PATH");
+ args->profile.reset(PROFILE_FD, "UNUSED_PATH");
args->swap_fd = SWAP_FD;
CallRunDex2Oat(std::move(args));
@@ -357,8 +354,8 @@
TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) {
auto args = RunDex2OatArgs::MakeDefaultTestArgs();
args->generate_compact_dex = true;
- args->input_vdex_fd = INPUT_VDEX_FD;
- args->output_vdex_fd = INPUT_VDEX_FD;
+ args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+ args->output_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
CallRunDex2Oat(std::move(args));
SetExpectedFlagUsed("--compact-dex-level", "=none");
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 96f5e44..fbf1e0c 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -663,7 +663,7 @@
&status);
EXPECT_STREQ(status.toString8().c_str(),
"Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for "
- "/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'");
+ "/data/app/com.installd.test.dexopt/base.jar failed: dex2oat error'");
}
TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) {
diff --git a/cmds/installd/unique_file.cpp b/cmds/installd/unique_file.cpp
new file mode 100644
index 0000000..e99ce1e
--- /dev/null
+++ b/cmds/installd/unique_file.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "unique_file.h"
+
+#include <string>
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace installd {
+
+UniqueFile::UniqueFile() : UniqueFile(-1, "") {}
+
+UniqueFile::UniqueFile(int value, std::string path) : UniqueFile(value, path, nullptr) {}
+
+UniqueFile::UniqueFile(int value, std::string path, CleanUpFunction cleanup)
+ : value_(value), path_(path), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
+
+UniqueFile::UniqueFile(UniqueFile&& other) {
+ *this = std::move(other);
+}
+
+UniqueFile::~UniqueFile() {
+ reset();
+}
+
+UniqueFile& UniqueFile::operator=(UniqueFile&& other) {
+ value_ = other.value_;
+ path_ = other.path_;
+ cleanup_ = other.cleanup_;
+ do_cleanup_ = other.do_cleanup_;
+ auto_close_ = other.auto_close_;
+ other.release();
+ return *this;
+}
+
+void UniqueFile::reset() {
+ reset(-1, "");
+}
+
+void UniqueFile::reset(int new_value, std::string path, CleanUpFunction new_cleanup) {
+ if (auto_close_ && value_ >= 0) {
+ if (close(value_) < 0) {
+ PLOG(ERROR) << "Failed to close fd " << value_ << ", with path " << path;
+ }
+ }
+ if (do_cleanup_ && cleanup_ != nullptr) {
+ cleanup_(path_);
+ }
+
+ value_ = new_value;
+ path_ = path;
+ cleanup_ = new_cleanup;
+}
+
+void UniqueFile::release() {
+ value_ = -1;
+ path_ = "";
+ do_cleanup_ = false;
+ cleanup_ = nullptr;
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/unique_file.h b/cmds/installd/unique_file.h
new file mode 100644
index 0000000..e85e23b
--- /dev/null
+++ b/cmds/installd/unique_file.h
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_INSTALLD_UNIQUE_FILE_H
+#define ANDROID_INSTALLD_UNIQUE_FILE_H
+
+#include <functional>
+#include <string>
+
+namespace android {
+namespace installd {
+
+// A file management helper that serves two purposes:
+//
+// 1. Closes the file description on destruction, similar unique_fd.
+// 2. Runs a cleanup function on after close, if not cancelled.
+//
+// The class does not assume the relationship between the given fd and file path.
+//
+// Example:
+//
+// UniqueFile file(open(...),
+// filepath,
+// [](const std::string& path) {
+// unlink(path.c_str());
+// });
+// if (file.fd() == -1) {
+// // Error opening...
+// }
+//
+// ...
+// if (error) {
+// // At this point, when the UniqueFile is destructed, the cleanup function will run
+// // (e.g. to delete the file) after the fd is closed.
+// return -1;
+// }
+//
+// (Success case)
+// file.DisableCleanup();
+// // At this point, when the UniqueFile is destructed, the cleanup function will not run
+// // (e.g. leaving the file around) after the fd is closed.
+//
+class UniqueFile {
+ private:
+ using CleanUpFunction = std::function<void (const std::string&)>;
+
+ public:
+ UniqueFile();
+ UniqueFile(int value, std::string path);
+ UniqueFile(int value, std::string path, CleanUpFunction cleanup);
+ UniqueFile(UniqueFile&& other);
+ ~UniqueFile();
+
+ UniqueFile& operator=(UniqueFile&& other);
+
+ int fd() const {
+ return value_;
+ }
+
+ const std::string& path() const {
+ return path_;
+ }
+
+ void DisableAutoClose() {
+ auto_close_ = false;
+ }
+
+ void DisableCleanup() {
+ do_cleanup_ = false;
+ }
+
+ void reset();
+ void reset(int new_value, std::string path, CleanUpFunction new_cleanup = nullptr);
+
+ private:
+ void release();
+
+ int value_;
+ std::string path_;
+ CleanUpFunction cleanup_;
+ bool do_cleanup_;
+ bool auto_close_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_UNIQUE_FILE_H
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index 2c3efe5..99cb93a 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -141,7 +141,7 @@
}
}
- PipeRelay relay(out);
+ PipeRelay relay(out, err, interfaceName, instanceName);
if (relay.initCheck() != OK) {
std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 820679f..4e97636 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -23,7 +23,6 @@
#include <atomic>
-#include <android-base/logging.h>
#include <utils/Thread.h>
namespace android {
@@ -31,8 +30,15 @@
static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
+static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
+ auto dot = interfaceName.rfind(".");
+ if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
+ return "RelayThread_" + interfaceName + "_" + instanceName;
+}
+
struct PipeRelay::RelayThread : public Thread {
- explicit RelayThread(int fd, std::ostream &os);
+ explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &fqName);
bool threadLoop() override;
void setFinished();
@@ -40,6 +46,7 @@
private:
int mFd;
std::ostream &mOutStream;
+ NullableOStream<std::ostream> mErrStream;
// If we were to use requestExit() and exitPending() instead, threadLoop()
// may not run at all by the time ~PipeRelay is called (i.e. debug() has
@@ -47,13 +54,17 @@
// read() are executed until data are drained.
std::atomic_bool mFinished;
+ std::string mFqName;
+
DISALLOW_COPY_AND_ASSIGN(RelayThread);
};
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os)
- : mFd(fd), mOutStream(os), mFinished(false) {}
+PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
+ const NullableOStream<std::ostream> &err,
+ const std::string &fqName)
+ : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
bool PipeRelay::RelayThread::threadLoop() {
char buffer[1024];
@@ -66,13 +77,14 @@
int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
if (res < 0) {
- PLOG(INFO) << "select() failed";
+ mErrStream << "debug " << mFqName << ": select() failed";
return false;
}
if (res == 0 || !FD_ISSET(mFd, &set)) {
if (mFinished) {
- LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated.";
+ mErrStream << "debug " << mFqName
+ << ": timeout reading from pipe, output may be truncated.";
return false;
}
// timeout, but debug() has not returned, so wait for HAL to finish.
@@ -83,7 +95,7 @@
ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
if (n < 0) {
- PLOG(ERROR) << "read() failed";
+ mErrStream << "debug " << mFqName << ": read() failed";
}
if (n <= 0) {
@@ -101,8 +113,9 @@
////////////////////////////////////////////////////////////////////////////////
-PipeRelay::PipeRelay(std::ostream &os)
- : mInitCheck(NO_INIT) {
+PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
+ const std::string &interfaceName, const std::string &instanceName)
+ : mInitCheck(NO_INIT) {
int res = pipe(mFds);
if (res < 0) {
@@ -110,8 +123,8 @@
return;
}
- mThread = new RelayThread(mFds[0], os);
- mInitCheck = mThread->run("RelayThread");
+ mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
+ mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
}
void PipeRelay::CloseFd(int *fd) {
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index 835016041..bd994b4 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -21,6 +21,8 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include "NullableOStream.h"
+
namespace android {
namespace lshal {
@@ -28,7 +30,10 @@
* written to the "write"-end of the pair to the specified output stream "os".
*/
struct PipeRelay {
- explicit PipeRelay(std::ostream &os);
+ explicit PipeRelay(std::ostream& os,
+ const NullableOStream<std::ostream>& err,
+ const std::string& interfaceName,
+ const std::string& instanceName);
~PipeRelay();
status_t initCheck() const;
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 7277e85..b139251 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,6 +44,9 @@
cflags: [
"-DVENDORSERVICEMANAGER=1",
],
+ required: [
+ "vndservice",
+ ],
srcs: ["main.cpp"],
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index e80c321..7aac7da 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -37,6 +37,27 @@
namespace android {
#ifndef VENDORSERVICEMANAGER
+struct ManifestWithDescription {
+ std::shared_ptr<const vintf::HalManifest> manifest;
+ const char* description;
+};
+// func true -> stop search and forEachManifest will return true
+static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
+ for (const ManifestWithDescription& mwd : {
+ ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
+ ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
+ }) {
+ 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;
+ }
+ if (func(mwd)) return true;
+ }
+ return false;
+}
+
static bool isVintfDeclared(const std::string& name) {
size_t firstSlash = name.find('/');
size_t lastDot = name.rfind('.', firstSlash);
@@ -49,31 +70,41 @@
const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
const std::string instance = name.substr(firstSlash+1);
- struct ManifestWithDescription {
- std::shared_ptr<const vintf::HalManifest> manifest;
- const char* description;
- };
- for (const ManifestWithDescription& mwd : {
- ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
- ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
- }) {
- 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;
- }
+ bool found = forEachManifest([&] (const ManifestWithDescription& mwd) {
if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
return true;
}
+ return false; // continue
+ });
+
+ if (!found) {
+ // Although it is tested, explicitly rebuilding qualified name, in case it
+ // becomes something unexpected.
+ LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
+ << " in the VINTF manifest.";
}
- // Although it is tested, explicitly rebuilding qualified name, in case it
- // becomes something unexpected.
- LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
- << " in the VINTF manifest.";
- return false;
+ return found;
+}
+
+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;
+ return {};
+ }
+ const std::string package = interface.substr(0, lastDot);
+ const std::string iface = interface.substr(lastDot+1);
+
+ std::vector<std::string> ret;
+ (void)forEachManifest([&](const ManifestWithDescription& mwd) {
+ auto instances = mwd.manifest->getAidlInstances(package, iface);
+ ret.insert(ret.end(), instances.begin(), instances.end());
+ return false; // continue
+ });
+
+ return ret;
}
static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
@@ -331,6 +362,29 @@
return Status::ok();
}
+binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
+ auto ctx = mAccess->getCallingContext();
+
+ std::vector<std::string> allInstances;
+#ifndef VENDORSERVICEMANAGER
+ allInstances = getVintfInstances(interface);
+#endif
+
+ outReturn->clear();
+
+ for (const std::string& instance : allInstances) {
+ if (mAccess->canFind(ctx, interface + "/" + instance)) {
+ outReturn->push_back(instance);
+ }
+ }
+
+ if (outReturn->size() == 0 && allInstances.size() != 0) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ return Status::ok();
+}
+
void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index a2fc5a8..9f43eb4 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,6 +44,7 @@
const sp<IServiceCallback>& callback) override;
binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) override;
binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) override;
binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 2618906..b1bc6dc 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -130,7 +130,7 @@
}
IPCThreadState::self()->setTheContextObject(manager);
- ps->becomeContextManager(nullptr, nullptr);
+ ps->becomeContextManager();
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS
index cc4c842..32bcc83 100644
--- a/cmds/surfacereplayer/OWNERS
+++ b/cmds/surfacereplayer/OWNERS
@@ -1,2 +1 @@
-mathias@google.com
-racarr@google.com
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 3ada05c..6c3b79b 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -25,9 +25,10 @@
repeated SurfaceChange surface_change = 1;
repeated DisplayChange display_change = 2;
- required bool synchronous = 3;
- required bool animation = 4;
- optional Origin origin = 5;
+ required bool synchronous = 3;
+ required bool animation = 4;
+ optional Origin origin = 5;
+ optional uint64 id = 6;
}
message SurfaceChange {
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 2b5667d..86e4f5d 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -584,9 +584,7 @@
return;
}
- auto handle = mLayers[dtc.layer_id()]->getHandle();
-
- t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number());
+ t.deferTransactionUntil_legacy(mLayers[id], mLayers[dtc.layer_id()], dtc.frame_number());
}
void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
@@ -706,11 +704,11 @@
void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ReparentChange& c) {
- sp<IBinder> newParentHandle = nullptr;
+ sp<SurfaceControl> newSurfaceControl = nullptr;
if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) {
- newParentHandle = mLayers[c.parent_id()]->getHandle();
+ newSurfaceControl = mLayers[c.parent_id()];
}
- t.reparent(mLayers[id], newParentHandle);
+ t.reparent(mLayers[id], newSurfaceControl);
}
void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t,
@@ -719,7 +717,7 @@
ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id());
return;
}
- t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()]->getHandle(), c.z());
+ t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z());
}
void Replayer::setDetachChildrenChange(SurfaceComposerClient::Transaction& t,
@@ -733,7 +731,7 @@
ALOGE("Layer %d not found in reparent children transaction", c.parent_id());
return;
}
- t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
+ t.reparentChildren(mLayers[id], mLayers[c.parent_id()]);
}
void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 50f117d..ccf4dc8 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,12 +38,12 @@
<!-- basic system services -->
<feature name="android.software.connectionservice" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
- <feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.secure_lock_screen" />
+ <feature name="android.software.input_methods" />
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
diff --git a/include/android/input.h b/include/android/input.h
index 7c39234..b04775b 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -988,11 +988,9 @@
int64_t AKeyEvent_getEventTime(const AInputEvent* key_event);
/**
- * Creates a native AInputEvent* object associated with the specified Java android.view.KeyEvent.
- * The result may be used with generic and KeyEvent-specific AInputEvent_* functions.
- * The object returned by this function must be disposed using {@link AInputEvent_release()}.
- * User must guarantee that lifetime for object referenced by keyEvent is prolongated
- * up to release of returned AInputEvent*.
+ * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent.
+ * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object
+ * returned by this function must be disposed using {@link AInputEvent_release()}.
*/
const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent);
@@ -1312,11 +1310,10 @@
int32_t axis, size_t pointer_index, size_t history_index);
/**
- * Creates a native AInputEvent* object associated with the specified Java android.view.MotionEvent.
- * The result may be used with generic and MotionEvent-specific AInputEvent_* functions.
- * The object returned by this function must be disposed using {@link AInputEvent_release()}.
- * User must guarantee that object referenced by motionEvent won't be recycled and
- * its lifetime is prolongated up to release of returned AInputEvent*.
+ * Creates a native AInputEvent* object that is a copy of the specified Java
+ * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific
+ * AInputEvent_* functions. The object returned by this function must be disposed using
+ * {@link AInputEvent_release()}.
*/
const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent);
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 6efa4f7..5f74682 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -65,6 +65,10 @@
* another process. File descriptors may also be sent to other processes over a Unix domain
* socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
*
+ * If you intend to share this file descriptor with a child process after
+ * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD
+ * to clear the FD_CLOEXEC flag for this to work on all versions of Android.
+ *
* Available since API level 26.
*
* \param name an optional name.
diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h
new file mode 100644
index 0000000..571a361
--- /dev/null
+++ b/include/attestation/HmacKeyManager.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <array>
+
+namespace android {
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
+class HmacKeyManager {
+public:
+ HmacKeyManager();
+ std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
+private:
+ const std::array<uint8_t, 128> mHmacKey;
+};
+} // namespace android
\ No newline at end of file
diff --git a/include/binder/Enum.h b/include/binder/Enum.h
new file mode 100644
index 0000000..4c25654
--- /dev/null
+++ b/include/binder/Enum.h
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#error Do not rely on global include files. All Android cc_* programs are given access to \
+ include_dirs for frameworks/native/include via global configuration, but this is legacy \
+ configuration. Instead, you should have a direct dependency on libbinder OR one of your \
+ dependencies should re-export libbinder headers with export_shared_lib_headers.
diff --git a/include/input/Flags.h b/include/input/Flags.h
index 4ad9056..072dd18 100644
--- a/include/input/Flags.h
+++ b/include/input/Flags.h
@@ -22,6 +22,7 @@
#include <string>
#include <type_traits>
+#include "NamedEnum.h"
#include "utils/BitSet.h"
#ifndef __UI_INPUT_FLAGS_H
@@ -30,38 +31,6 @@
namespace android {
namespace details {
-template <typename F, F V>
-constexpr std::optional<std::string_view> enum_value_name() {
- // Should look something like (but all on one line):
- // std::optional<std::string_view>
- // android::details::enum_value_name()
- // [F = android::test::TestFlags, V = android::test::TestFlags::ONE]
- std::string_view view = __PRETTY_FUNCTION__;
- size_t templateStart = view.rfind("[");
- size_t templateEnd = view.rfind("]");
- if (templateStart == std::string::npos || templateEnd == std::string::npos) {
- return std::nullopt;
- }
-
- // Extract the template parameters without the enclosing braces.
- // Example (cont'd): F = android::test::TestFlags, V = android::test::TestFlags::ONE
- view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
- size_t valStart = view.rfind("V = ");
- if (valStart == std::string::npos) {
- return std::nullopt;
- }
-
- // Example (cont'd): V = android::test::TestFlags::ONE
- view = view.substr(valStart);
- size_t nameStart = view.rfind("::");
- if (nameStart == std::string::npos) {
- return std::nullopt;
- }
-
- // Chop off the initial "::"
- nameStart += 2;
- return view.substr(nameStart);
-}
template <typename F>
inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
diff --git a/include/input/Input.h b/include/input/Input.h
index d40ba43..7d81fed 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -141,14 +141,6 @@
#define MAX_CONTROLLER_LEDS 4
/*
- * SystemUiVisibility constants from View.
- */
-enum {
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
- ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
-};
-
-/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
@@ -312,11 +304,6 @@
*/
constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
-/**
- * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
- */
-constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
-
/*
* Pointer coordinate data.
*/
@@ -583,10 +570,18 @@
float getAxisValue(int32_t axis, size_t pointerIndex) const;
+ /**
+ * Get the X coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
+ * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
+ */
inline float getX(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);
}
+ /**
+ * Get the Y coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.
+ * Identical to calling getHistoricalX(pointerIndex, getHistorySize()).
+ */
inline float getY(size_t pointerIndex) const {
return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex);
}
@@ -727,7 +722,7 @@
inline const PointerProperties* getPointerProperties() const {
return mPointerProperties.array();
}
- inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
+ inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.data(); }
inline const PointerCoords* getSamplePointerCoords() const {
return mSamplePointerCoords.array();
}
@@ -752,7 +747,7 @@
float mRawYCursorPosition;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
- Vector<nsecs_t> mSampleEventTimes;
+ std::vector<nsecs_t> mSampleEventTimes;
Vector<PointerCoords> mSamplePointerCoords;
};
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index c7685b7..60638ca 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -108,11 +108,11 @@
inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
inline int32_t getKeyboardType() const { return mKeyboardType; }
- inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
+ inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
mKeyCharacterMap = value;
}
- inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+ inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
return mKeyCharacterMap;
}
@@ -136,7 +136,7 @@
bool mHasMic;
uint32_t mSources;
int32_t mKeyboardType;
- sp<KeyCharacterMap> mKeyCharacterMap;
+ std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
bool mHasVibrator;
bool mHasButtonUnderPad;
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index ec2de61..2a742f9 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -21,11 +21,6 @@
#include <android/keycodes.h>
#include <unordered_map>
-#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
-#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
-#define DEFINE_LED(led) { #led, ALED_##led }
-#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag }
-
namespace android {
template<typename T, size_t N>
@@ -36,437 +31,41 @@
int value;
};
-// NOTE: If you add a new keycode here you must also add it to several other files.
-// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
-#define KEYCODES_SEQUENCE \
- DEFINE_KEYCODE(UNKNOWN), \
- DEFINE_KEYCODE(SOFT_LEFT), \
- DEFINE_KEYCODE(SOFT_RIGHT), \
- DEFINE_KEYCODE(HOME), \
- DEFINE_KEYCODE(BACK), \
- DEFINE_KEYCODE(CALL), \
- DEFINE_KEYCODE(ENDCALL), \
- DEFINE_KEYCODE(0), \
- DEFINE_KEYCODE(1), \
- DEFINE_KEYCODE(2), \
- DEFINE_KEYCODE(3), \
- DEFINE_KEYCODE(4), \
- DEFINE_KEYCODE(5), \
- DEFINE_KEYCODE(6), \
- DEFINE_KEYCODE(7), \
- DEFINE_KEYCODE(8), \
- DEFINE_KEYCODE(9), \
- DEFINE_KEYCODE(STAR), \
- DEFINE_KEYCODE(POUND), \
- DEFINE_KEYCODE(DPAD_UP), \
- DEFINE_KEYCODE(DPAD_DOWN), \
- DEFINE_KEYCODE(DPAD_LEFT), \
- DEFINE_KEYCODE(DPAD_RIGHT), \
- DEFINE_KEYCODE(DPAD_CENTER), \
- DEFINE_KEYCODE(VOLUME_UP), \
- DEFINE_KEYCODE(VOLUME_DOWN), \
- DEFINE_KEYCODE(POWER), \
- DEFINE_KEYCODE(CAMERA), \
- DEFINE_KEYCODE(CLEAR), \
- DEFINE_KEYCODE(A), \
- DEFINE_KEYCODE(B), \
- DEFINE_KEYCODE(C), \
- DEFINE_KEYCODE(D), \
- DEFINE_KEYCODE(E), \
- DEFINE_KEYCODE(F), \
- DEFINE_KEYCODE(G), \
- DEFINE_KEYCODE(H), \
- DEFINE_KEYCODE(I), \
- DEFINE_KEYCODE(J), \
- DEFINE_KEYCODE(K), \
- DEFINE_KEYCODE(L), \
- DEFINE_KEYCODE(M), \
- DEFINE_KEYCODE(N), \
- DEFINE_KEYCODE(O), \
- DEFINE_KEYCODE(P), \
- DEFINE_KEYCODE(Q), \
- DEFINE_KEYCODE(R), \
- DEFINE_KEYCODE(S), \
- DEFINE_KEYCODE(T), \
- DEFINE_KEYCODE(U), \
- DEFINE_KEYCODE(V), \
- DEFINE_KEYCODE(W), \
- DEFINE_KEYCODE(X), \
- DEFINE_KEYCODE(Y), \
- DEFINE_KEYCODE(Z), \
- DEFINE_KEYCODE(COMMA), \
- DEFINE_KEYCODE(PERIOD), \
- DEFINE_KEYCODE(ALT_LEFT), \
- DEFINE_KEYCODE(ALT_RIGHT), \
- DEFINE_KEYCODE(SHIFT_LEFT), \
- DEFINE_KEYCODE(SHIFT_RIGHT), \
- DEFINE_KEYCODE(TAB), \
- DEFINE_KEYCODE(SPACE), \
- DEFINE_KEYCODE(SYM), \
- DEFINE_KEYCODE(EXPLORER), \
- DEFINE_KEYCODE(ENVELOPE), \
- DEFINE_KEYCODE(ENTER), \
- DEFINE_KEYCODE(DEL), \
- DEFINE_KEYCODE(GRAVE), \
- DEFINE_KEYCODE(MINUS), \
- DEFINE_KEYCODE(EQUALS), \
- DEFINE_KEYCODE(LEFT_BRACKET), \
- DEFINE_KEYCODE(RIGHT_BRACKET), \
- DEFINE_KEYCODE(BACKSLASH), \
- DEFINE_KEYCODE(SEMICOLON), \
- DEFINE_KEYCODE(APOSTROPHE), \
- DEFINE_KEYCODE(SLASH), \
- DEFINE_KEYCODE(AT), \
- DEFINE_KEYCODE(NUM), \
- DEFINE_KEYCODE(HEADSETHOOK), \
- DEFINE_KEYCODE(FOCUS), \
- DEFINE_KEYCODE(PLUS), \
- DEFINE_KEYCODE(MENU), \
- DEFINE_KEYCODE(NOTIFICATION), \
- DEFINE_KEYCODE(SEARCH), \
- DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \
- DEFINE_KEYCODE(MEDIA_STOP), \
- DEFINE_KEYCODE(MEDIA_NEXT), \
- DEFINE_KEYCODE(MEDIA_PREVIOUS), \
- DEFINE_KEYCODE(MEDIA_REWIND), \
- DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \
- DEFINE_KEYCODE(MUTE), \
- DEFINE_KEYCODE(PAGE_UP), \
- DEFINE_KEYCODE(PAGE_DOWN), \
- DEFINE_KEYCODE(PICTSYMBOLS), \
- DEFINE_KEYCODE(SWITCH_CHARSET), \
- DEFINE_KEYCODE(BUTTON_A), \
- DEFINE_KEYCODE(BUTTON_B), \
- DEFINE_KEYCODE(BUTTON_C), \
- DEFINE_KEYCODE(BUTTON_X), \
- DEFINE_KEYCODE(BUTTON_Y), \
- DEFINE_KEYCODE(BUTTON_Z), \
- DEFINE_KEYCODE(BUTTON_L1), \
- DEFINE_KEYCODE(BUTTON_R1), \
- DEFINE_KEYCODE(BUTTON_L2), \
- DEFINE_KEYCODE(BUTTON_R2), \
- DEFINE_KEYCODE(BUTTON_THUMBL), \
- DEFINE_KEYCODE(BUTTON_THUMBR), \
- DEFINE_KEYCODE(BUTTON_START), \
- DEFINE_KEYCODE(BUTTON_SELECT), \
- DEFINE_KEYCODE(BUTTON_MODE), \
- DEFINE_KEYCODE(ESCAPE), \
- DEFINE_KEYCODE(FORWARD_DEL), \
- DEFINE_KEYCODE(CTRL_LEFT), \
- DEFINE_KEYCODE(CTRL_RIGHT), \
- DEFINE_KEYCODE(CAPS_LOCK), \
- DEFINE_KEYCODE(SCROLL_LOCK), \
- DEFINE_KEYCODE(META_LEFT), \
- DEFINE_KEYCODE(META_RIGHT), \
- DEFINE_KEYCODE(FUNCTION), \
- DEFINE_KEYCODE(SYSRQ), \
- DEFINE_KEYCODE(BREAK), \
- DEFINE_KEYCODE(MOVE_HOME), \
- DEFINE_KEYCODE(MOVE_END), \
- DEFINE_KEYCODE(INSERT), \
- DEFINE_KEYCODE(FORWARD), \
- DEFINE_KEYCODE(MEDIA_PLAY), \
- DEFINE_KEYCODE(MEDIA_PAUSE), \
- DEFINE_KEYCODE(MEDIA_CLOSE), \
- DEFINE_KEYCODE(MEDIA_EJECT), \
- DEFINE_KEYCODE(MEDIA_RECORD), \
- DEFINE_KEYCODE(F1), \
- DEFINE_KEYCODE(F2), \
- DEFINE_KEYCODE(F3), \
- DEFINE_KEYCODE(F4), \
- DEFINE_KEYCODE(F5), \
- DEFINE_KEYCODE(F6), \
- DEFINE_KEYCODE(F7), \
- DEFINE_KEYCODE(F8), \
- DEFINE_KEYCODE(F9), \
- DEFINE_KEYCODE(F10), \
- DEFINE_KEYCODE(F11), \
- DEFINE_KEYCODE(F12), \
- DEFINE_KEYCODE(NUM_LOCK), \
- DEFINE_KEYCODE(NUMPAD_0), \
- DEFINE_KEYCODE(NUMPAD_1), \
- DEFINE_KEYCODE(NUMPAD_2), \
- DEFINE_KEYCODE(NUMPAD_3), \
- DEFINE_KEYCODE(NUMPAD_4), \
- DEFINE_KEYCODE(NUMPAD_5), \
- DEFINE_KEYCODE(NUMPAD_6), \
- DEFINE_KEYCODE(NUMPAD_7), \
- DEFINE_KEYCODE(NUMPAD_8), \
- DEFINE_KEYCODE(NUMPAD_9), \
- DEFINE_KEYCODE(NUMPAD_DIVIDE), \
- DEFINE_KEYCODE(NUMPAD_MULTIPLY), \
- DEFINE_KEYCODE(NUMPAD_SUBTRACT), \
- DEFINE_KEYCODE(NUMPAD_ADD), \
- DEFINE_KEYCODE(NUMPAD_DOT), \
- DEFINE_KEYCODE(NUMPAD_COMMA), \
- DEFINE_KEYCODE(NUMPAD_ENTER), \
- DEFINE_KEYCODE(NUMPAD_EQUALS), \
- DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \
- DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \
- DEFINE_KEYCODE(VOLUME_MUTE), \
- DEFINE_KEYCODE(INFO), \
- DEFINE_KEYCODE(CHANNEL_UP), \
- DEFINE_KEYCODE(CHANNEL_DOWN), \
- DEFINE_KEYCODE(ZOOM_IN), \
- DEFINE_KEYCODE(ZOOM_OUT), \
- DEFINE_KEYCODE(TV), \
- DEFINE_KEYCODE(WINDOW), \
- DEFINE_KEYCODE(GUIDE), \
- DEFINE_KEYCODE(DVR), \
- DEFINE_KEYCODE(BOOKMARK), \
- DEFINE_KEYCODE(CAPTIONS), \
- DEFINE_KEYCODE(SETTINGS), \
- DEFINE_KEYCODE(TV_POWER), \
- DEFINE_KEYCODE(TV_INPUT), \
- DEFINE_KEYCODE(STB_POWER), \
- DEFINE_KEYCODE(STB_INPUT), \
- DEFINE_KEYCODE(AVR_POWER), \
- DEFINE_KEYCODE(AVR_INPUT), \
- DEFINE_KEYCODE(PROG_RED), \
- DEFINE_KEYCODE(PROG_GREEN), \
- DEFINE_KEYCODE(PROG_YELLOW), \
- DEFINE_KEYCODE(PROG_BLUE), \
- DEFINE_KEYCODE(APP_SWITCH), \
- DEFINE_KEYCODE(BUTTON_1), \
- DEFINE_KEYCODE(BUTTON_2), \
- DEFINE_KEYCODE(BUTTON_3), \
- DEFINE_KEYCODE(BUTTON_4), \
- DEFINE_KEYCODE(BUTTON_5), \
- DEFINE_KEYCODE(BUTTON_6), \
- DEFINE_KEYCODE(BUTTON_7), \
- DEFINE_KEYCODE(BUTTON_8), \
- DEFINE_KEYCODE(BUTTON_9), \
- DEFINE_KEYCODE(BUTTON_10), \
- DEFINE_KEYCODE(BUTTON_11), \
- DEFINE_KEYCODE(BUTTON_12), \
- DEFINE_KEYCODE(BUTTON_13), \
- DEFINE_KEYCODE(BUTTON_14), \
- DEFINE_KEYCODE(BUTTON_15), \
- DEFINE_KEYCODE(BUTTON_16), \
- DEFINE_KEYCODE(LANGUAGE_SWITCH), \
- DEFINE_KEYCODE(MANNER_MODE), \
- DEFINE_KEYCODE(3D_MODE), \
- DEFINE_KEYCODE(CONTACTS), \
- DEFINE_KEYCODE(CALENDAR), \
- DEFINE_KEYCODE(MUSIC), \
- DEFINE_KEYCODE(CALCULATOR), \
- DEFINE_KEYCODE(ZENKAKU_HANKAKU), \
- DEFINE_KEYCODE(EISU), \
- DEFINE_KEYCODE(MUHENKAN), \
- DEFINE_KEYCODE(HENKAN), \
- DEFINE_KEYCODE(KATAKANA_HIRAGANA), \
- DEFINE_KEYCODE(YEN), \
- DEFINE_KEYCODE(RO), \
- DEFINE_KEYCODE(KANA), \
- DEFINE_KEYCODE(ASSIST), \
- DEFINE_KEYCODE(BRIGHTNESS_DOWN), \
- DEFINE_KEYCODE(BRIGHTNESS_UP), \
- DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \
- DEFINE_KEYCODE(SLEEP), \
- DEFINE_KEYCODE(WAKEUP), \
- DEFINE_KEYCODE(PAIRING), \
- DEFINE_KEYCODE(MEDIA_TOP_MENU), \
- DEFINE_KEYCODE(11), \
- DEFINE_KEYCODE(12), \
- DEFINE_KEYCODE(LAST_CHANNEL), \
- DEFINE_KEYCODE(TV_DATA_SERVICE), \
- DEFINE_KEYCODE(VOICE_ASSIST), \
- DEFINE_KEYCODE(TV_RADIO_SERVICE), \
- DEFINE_KEYCODE(TV_TELETEXT), \
- DEFINE_KEYCODE(TV_NUMBER_ENTRY), \
- DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \
- DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \
- DEFINE_KEYCODE(TV_SATELLITE), \
- DEFINE_KEYCODE(TV_SATELLITE_BS), \
- DEFINE_KEYCODE(TV_SATELLITE_CS), \
- DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \
- DEFINE_KEYCODE(TV_NETWORK), \
- DEFINE_KEYCODE(TV_ANTENNA_CABLE), \
- DEFINE_KEYCODE(TV_INPUT_HDMI_1), \
- DEFINE_KEYCODE(TV_INPUT_HDMI_2), \
- DEFINE_KEYCODE(TV_INPUT_HDMI_3), \
- DEFINE_KEYCODE(TV_INPUT_HDMI_4), \
- DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \
- DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \
- DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \
- DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \
- DEFINE_KEYCODE(TV_INPUT_VGA_1), \
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \
- DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \
- DEFINE_KEYCODE(TV_ZOOM_MODE), \
- DEFINE_KEYCODE(TV_CONTENTS_MENU), \
- DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \
- DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \
- DEFINE_KEYCODE(HELP), \
- DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \
- DEFINE_KEYCODE(NAVIGATE_NEXT), \
- DEFINE_KEYCODE(NAVIGATE_IN), \
- DEFINE_KEYCODE(NAVIGATE_OUT), \
- DEFINE_KEYCODE(STEM_PRIMARY), \
- DEFINE_KEYCODE(STEM_1), \
- DEFINE_KEYCODE(STEM_2), \
- DEFINE_KEYCODE(STEM_3), \
- DEFINE_KEYCODE(DPAD_UP_LEFT), \
- DEFINE_KEYCODE(DPAD_DOWN_LEFT), \
- DEFINE_KEYCODE(DPAD_UP_RIGHT), \
- DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \
- DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \
- DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \
- DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \
- DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \
- DEFINE_KEYCODE(SOFT_SLEEP), \
- DEFINE_KEYCODE(CUT), \
- DEFINE_KEYCODE(COPY), \
- DEFINE_KEYCODE(PASTE), \
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \
- DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \
- DEFINE_KEYCODE(ALL_APPS), \
- DEFINE_KEYCODE(REFRESH), \
- DEFINE_KEYCODE(THUMBS_UP), \
- DEFINE_KEYCODE(THUMBS_DOWN), \
- DEFINE_KEYCODE(PROFILE_SWITCH)
+// NOTE: If you want a new key code, axis code, led code or flag code in keylayout file,
+// then you must add it to InputEventLabels.cpp.
-static const std::unordered_map<std::string, int> KEYCODES = {
- KEYCODES_SEQUENCE
+class InputEventLookup {
+public:
+ static int lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+ const char* literal);
+
+ static const char* lookupLabelByValue(const std::vector<InputEventLabel>& vec, int value);
+
+ static int32_t getKeyCodeByLabel(const char* label);
+
+ static const char* getLabelByKeyCode(int32_t keyCode);
+
+ static uint32_t getKeyFlagByLabel(const char* label);
+
+ static int32_t getAxisByLabel(const char* label);
+
+ static const char* getAxisLabel(int32_t axisId);
+
+ static int32_t getLedByLabel(const char* label);
+
+private:
+ static const std::unordered_map<std::string, int> KEYCODES;
+
+ static const std::vector<InputEventLabel> KEY_NAMES;
+
+ static const std::unordered_map<std::string, int> AXES;
+
+ static const std::vector<InputEventLabel> AXES_NAMES;
+
+ static const std::unordered_map<std::string, int> LEDS;
+
+ static const std::unordered_map<std::string, int> FLAGS;
};
-static const std::vector<InputEventLabel> KEY_NAMES = {
- KEYCODES_SEQUENCE
-};
-
-// 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.
-#define AXES_SEQUENCE \
- DEFINE_AXIS(X), \
- DEFINE_AXIS(Y), \
- DEFINE_AXIS(PRESSURE), \
- DEFINE_AXIS(SIZE), \
- DEFINE_AXIS(TOUCH_MAJOR), \
- DEFINE_AXIS(TOUCH_MINOR), \
- DEFINE_AXIS(TOOL_MAJOR), \
- DEFINE_AXIS(TOOL_MINOR), \
- DEFINE_AXIS(ORIENTATION), \
- DEFINE_AXIS(VSCROLL), \
- DEFINE_AXIS(HSCROLL), \
- DEFINE_AXIS(Z), \
- DEFINE_AXIS(RX), \
- DEFINE_AXIS(RY), \
- DEFINE_AXIS(RZ), \
- DEFINE_AXIS(HAT_X), \
- DEFINE_AXIS(HAT_Y), \
- DEFINE_AXIS(LTRIGGER), \
- DEFINE_AXIS(RTRIGGER), \
- DEFINE_AXIS(THROTTLE), \
- DEFINE_AXIS(RUDDER), \
- DEFINE_AXIS(WHEEL), \
- DEFINE_AXIS(GAS), \
- DEFINE_AXIS(BRAKE), \
- DEFINE_AXIS(DISTANCE), \
- DEFINE_AXIS(TILT), \
- DEFINE_AXIS(SCROLL), \
- DEFINE_AXIS(RELATIVE_X), \
- DEFINE_AXIS(RELATIVE_Y), \
- DEFINE_AXIS(GENERIC_1), \
- DEFINE_AXIS(GENERIC_2), \
- DEFINE_AXIS(GENERIC_3), \
- DEFINE_AXIS(GENERIC_4), \
- DEFINE_AXIS(GENERIC_5), \
- DEFINE_AXIS(GENERIC_6), \
- DEFINE_AXIS(GENERIC_7), \
- DEFINE_AXIS(GENERIC_8), \
- DEFINE_AXIS(GENERIC_9), \
- DEFINE_AXIS(GENERIC_10), \
- DEFINE_AXIS(GENERIC_11), \
- DEFINE_AXIS(GENERIC_12), \
- DEFINE_AXIS(GENERIC_13), \
- DEFINE_AXIS(GENERIC_14), \
- DEFINE_AXIS(GENERIC_15), \
- DEFINE_AXIS(GENERIC_16)
-
-static const std::unordered_map<std::string, int> AXES = {
- AXES_SEQUENCE
-};
-
-static const std::vector<InputEventLabel> AXES_NAMES = {
- AXES_SEQUENCE
-};
-
-// NOTE: If you add new LEDs here, you must also add them to Input.h
-#define LEDS_SEQUENCE \
- DEFINE_LED(NUM_LOCK), \
- DEFINE_LED(CAPS_LOCK), \
- DEFINE_LED(SCROLL_LOCK), \
- DEFINE_LED(COMPOSE), \
- DEFINE_LED(KANA), \
- DEFINE_LED(SLEEP), \
- DEFINE_LED(SUSPEND), \
- DEFINE_LED(MUTE), \
- DEFINE_LED(MISC), \
- DEFINE_LED(MAIL), \
- DEFINE_LED(CHARGING), \
- DEFINE_LED(CONTROLLER_1), \
- DEFINE_LED(CONTROLLER_2), \
- DEFINE_LED(CONTROLLER_3), \
- DEFINE_LED(CONTROLLER_4)
-
-static const std::unordered_map<std::string, int> LEDS = {
- LEDS_SEQUENCE
-};
-
-#define FLAGS_SEQUENCE \
- DEFINE_FLAG(VIRTUAL), \
- DEFINE_FLAG(FUNCTION), \
- DEFINE_FLAG(GESTURE), \
- DEFINE_FLAG(WAKE)
-
-static const std::unordered_map<std::string, int> FLAGS = {
- FLAGS_SEQUENCE
-};
-
-static int lookupValueByLabel(const std::unordered_map<std::string, int> &map,
- const char* literal) {
- std::string str(literal);
- auto it = map.find(str);
- return it != map.end() ? it->second : 0;
-}
-
-static const char* lookupLabelByValue(const std::vector<InputEventLabel> &vec, int value) {
- if (static_cast<size_t>(value) < vec.size()) {
- return vec[value].literal;
- }
- return nullptr;
-}
-
-static inline int32_t getKeyCodeByLabel(const char* label) {
- return int32_t(lookupValueByLabel(KEYCODES, label));
-}
-
-static inline const char* getLabelByKeyCode(int32_t keyCode) {
- if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
- return lookupLabelByValue(KEY_NAMES, keyCode);
- }
- return nullptr;
-}
-
-static inline uint32_t getKeyFlagByLabel(const char* label) {
- return uint32_t(lookupValueByLabel(FLAGS, label));
-}
-
-static inline int32_t getAxisByLabel(const char* label) {
- return int32_t(lookupValueByLabel(AXES, label));
-}
-
-static inline const char* getAxisLabel(int32_t axisId) {
- return lookupLabelByValue(AXES_NAMES, axisId);
-}
-
-static inline int32_t getLedByLabel(const char* label) {
- return int32_t(lookupValueByLabel(LEDS, label));
-}
-
-
} // namespace android
#endif // _LIBINPUT_INPUT_EVENT_LABELS_H
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8e00969..ad0a14e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -246,6 +246,8 @@
/* Return a new object that has a duplicate of this channel's fd. */
std::unique_ptr<InputChannel> dup() const;
+ void copyTo(InputChannel& outChannel) const;
+
status_t readFromParcel(const android::Parcel* parcel) override;
status_t writeToParcel(android::Parcel* parcel) const override;
@@ -277,6 +279,8 @@
}
private:
+ base::unique_fd dupFd() const;
+
std::string mName;
android::base::unique_fd mFd;
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 7372022..36097d6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_WINDOW_H
#define _UI_INPUT_WINDOW_H
+#include <android/os/TouchOcclusionMode.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <input/Flags.h>
@@ -30,6 +31,8 @@
#include "InputApplication.h"
+using android::os::TouchOcclusionMode;
+
namespace android {
/*
@@ -158,6 +161,10 @@
// in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
float globalScaleFactor = 1.0f;
+ // The opacity of this window, from 0.0 to 1.0 (inclusive).
+ // An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
+ float alpha;
+
// Transform applied to individual windows.
ui::Transform transform;
@@ -176,8 +183,10 @@
* motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
*/
bool trustedOverlay = false;
+ TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
int32_t ownerPid = -1;
int32_t ownerUid = -1;
+ std::string packageName;
Flags<Feature> inputFeatures;
int32_t displayId = ADISPLAY_ID_NONE;
int32_t portalToDisplayId = ADISPLAY_ID_NONE;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index a1a32a6..d874347 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -23,12 +23,12 @@
#include <binder/IBinder.h>
#endif
+#include <android-base/result.h>
#include <input/Input.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Tokenizer.h>
#include <utils/Unicode.h>
-#include <utils/RefBase.h>
// Maximum number of keys supported by KeyCharacterMaps
#define MAX_KEYS 8192
@@ -42,7 +42,7 @@
*
* This object is immutable after it has been loaded.
*/
-class KeyCharacterMap : public RefBase {
+class KeyCharacterMap {
public:
enum KeyboardType {
KEYBOARD_TYPE_UNKNOWN = 0,
@@ -74,18 +74,18 @@
};
/* Loads a key character map from a file. */
- static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap);
+ static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename,
+ Format format);
/* Loads a key character map from its string contents. */
- static status_t loadContents(const std::string& filename,
- const char* contents, Format format, sp<KeyCharacterMap>* outMap);
+ static base::Result<std::shared_ptr<KeyCharacterMap>> loadContents(const std::string& filename,
+ const char* contents,
+ Format format);
- /* Combines a base key character map and an overlay. */
- static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
- const sp<KeyCharacterMap>& overlay);
+ const std::string getLoadFileName() const;
- /* Returns an empty key character map. */
- static sp<KeyCharacterMap> empty();
+ /* Combines this key character map with an overlay. */
+ void combine(const KeyCharacterMap& overlay);
/* Gets the keyboard type. */
int32_t getKeyboardType() const;
@@ -136,13 +136,14 @@
#ifdef __ANDROID__
/* Reads a key map from a parcel. */
- static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
+ static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
/* Writes a key map to a parcel. */
void writeToParcel(Parcel* parcel) const;
#endif
-protected:
+ KeyCharacterMap(const KeyCharacterMap& other);
+
virtual ~KeyCharacterMap();
private:
@@ -224,16 +225,14 @@
status_t parseCharacterLiteral(char16_t* outCharacter);
};
- static sp<KeyCharacterMap> sEmpty;
-
KeyedVector<int32_t, Key*> mKeys;
int mType;
+ std::string mLoadFileName;
KeyedVector<int32_t, int32_t> mKeysByScanCode;
KeyedVector<int32_t, int32_t> mKeysByUsageCode;
KeyCharacterMap();
- KeyCharacterMap(const KeyCharacterMap& other);
bool getKey(int32_t keyCode, const Key** outKey) const;
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
@@ -242,7 +241,7 @@
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
- static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
+ static base::Result<std::shared_ptr<KeyCharacterMap>> load(Tokenizer* tokenizer, Format format);
static void addKey(Vector<KeyEvent>& outEvents,
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 26f3501..872dd45 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,11 +17,12 @@
#ifndef _LIBINPUT_KEY_LAYOUT_MAP_H
#define _LIBINPUT_KEY_LAYOUT_MAP_H
+#include <android-base/result.h>
#include <stdint.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
#include <utils/RefBase.h>
+#include <utils/Tokenizer.h>
namespace android {
@@ -60,9 +61,12 @@
*
* This object is immutable after it has been loaded.
*/
-class KeyLayoutMap : public RefBase {
+class KeyLayoutMap {
public:
- static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+ static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename,
+ const char* contents);
status_t mapKey(int32_t scanCode, int32_t usageCode,
int32_t* outKeyCode, uint32_t* outFlags) const;
@@ -71,8 +75,8 @@
status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
+ const std::string getLoadFileName() const;
-protected:
virtual ~KeyLayoutMap();
private:
@@ -91,6 +95,7 @@
KeyedVector<int32_t, AxisInfo> mAxes;
KeyedVector<int32_t, Led> mLedsByScanCode;
KeyedVector<int32_t, Led> mLedsByUsageCode;
+ std::string mLoadFileName;
KeyLayoutMap();
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 92da10c..08ad8c6 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -20,8 +20,8 @@
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
+#include <input/PropertyMap.h>
#include <utils/Errors.h>
-#include <utils/PropertyMap.h>
namespace android {
@@ -34,10 +34,10 @@
class KeyMap {
public:
std::string keyLayoutFile;
- sp<KeyLayoutMap> keyLayoutMap;
+ std::shared_ptr<KeyLayoutMap> keyLayoutMap;
std::string keyCharacterMapFile;
- sp<KeyCharacterMap> keyCharacterMap;
+ std::shared_ptr<KeyCharacterMap> keyCharacterMap;
KeyMap();
~KeyMap();
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
new file mode 100644
index 0000000..42cfb12
--- /dev/null
+++ b/include/input/NamedEnum.h
@@ -0,0 +1,128 @@
+/*
+ * 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 <android-base/stringprintf.h>
+
+#include <array>
+#include <cstdint>
+#include <optional>
+#include <string>
+
+#ifndef __UI_INPUT_NAMEDENUM_H
+#define __UI_INPUT_NAMEDENUM_H
+
+namespace android {
+
+namespace details {
+template <typename E, E V>
+constexpr std::optional<std::string_view> enum_value_name() {
+ // Should look something like (but all on one line):
+ // std::optional<std::string_view>
+ // android::details::enum_value_name()
+ // [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
+ std::string_view view = __PRETTY_FUNCTION__;
+ size_t templateStart = view.rfind("[");
+ size_t templateEnd = view.rfind("]");
+ if (templateStart == std::string::npos || templateEnd == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Extract the template parameters without the enclosing braces.
+ // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
+ view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
+ size_t valStart = view.rfind("V = ");
+ if (valStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Example (cont'd): V = android::test::TestEnums::ONE
+ view = view.substr(valStart);
+ size_t nameStart = view.rfind("::");
+ if (nameStart == std::string::npos) {
+ return std::nullopt;
+ }
+
+ // Chop off the initial "::"
+ nameStart += 2;
+ return view.substr(nameStart);
+}
+
+template <typename E, typename T, T... I>
+constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
+ constexpr size_t count = seq.size();
+
+ std::array<E, count> values{};
+ for (size_t i = 0, v = 0; v < count; ++i) {
+ values[v++] = static_cast<E>(T{0} + i);
+ }
+
+ return values;
+}
+
+template <typename E, std::size_t N>
+inline constexpr auto enum_values =
+ generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});
+
+template <typename E, std::size_t N, std::size_t... I>
+constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
+ return std::array<std::optional<std::string_view>, sizeof...(I)>{
+ {enum_value_name<E, enum_values<E, N>[I]>()...}};
+}
+
+template <typename E, std::size_t N>
+inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});
+
+} // namespace details
+
+class NamedEnum {
+public:
+ // By default allowed enum value range is 0 ~ 7.
+ template <typename E>
+ static constexpr size_t max = 8;
+
+ template <auto V>
+ static constexpr auto enum_name() {
+ using E = decltype(V);
+ return details::enum_value_name<E, V>();
+ }
+
+ template <typename E>
+ static constexpr std::optional<std::string_view> enum_name(E val) {
+ auto idx = static_cast<size_t>(val);
+ return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
+ }
+
+ // Helper function for parsing enum value to string.
+ // Example : enum class TestEnums { ZERO = 0x0 };
+ // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
+ // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
+ // it should be declared to specialized the maximum enum by below:
+ // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
+ // If the enum class definition is sparse and contains enum values starting from a large value,
+ // Do not specialize it to a large number to avoid performance issues.
+ // The recommended maximum enum number to specialize is 64.
+ template <typename E>
+ static const std::string string(E val) {
+ std::string result;
+ std::optional<std::string_view> enumString = enum_name(val);
+ result += enumString ? enumString.value() : base::StringPrintf("0x%08x", val);
+ return result;
+ }
+};
+
+} // namespace android
+
+#endif // __UI_INPUT_NAMEDENUM_H
\ No newline at end of file
diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h
new file mode 100644
index 0000000..451918b
--- /dev/null
+++ b/include/input/PropertyMap.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 _UTILS_PROPERTY_MAP_H
+#define _UTILS_PROPERTY_MAP_H
+
+#include <android-base/result.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Tokenizer.h>
+
+namespace android {
+
+/*
+ * Provides a mechanism for passing around string-based property key / value pairs
+ * and loading them from property files.
+ *
+ * The property files have the following simple structure:
+ *
+ * # Comment
+ * key = value
+ *
+ * Keys and values are any sequence of printable ASCII characters.
+ * The '=' separates the key from the value.
+ * The key and value may not contain whitespace.
+ *
+ * The '\' character is reserved for escape sequences and is not currently supported.
+ * The '"" character is reserved for quoting and is not currently supported.
+ * Files that contain the '\' or '"' character will fail to parse.
+ *
+ * The file must not contain duplicate keys.
+ *
+ * TODO Support escape sequences and quoted values when needed.
+ */
+class PropertyMap {
+public:
+ /* Creates an empty property map. */
+ PropertyMap();
+ ~PropertyMap();
+
+ /* Clears the property map. */
+ void clear();
+
+ /* Adds a property.
+ * Replaces the property with the same key if it is already present.
+ */
+ void addProperty(const String8& key, const String8& value);
+
+ /* Returns true if the property map contains the specified key. */
+ bool hasProperty(const String8& key) const;
+
+ /* Gets the value of a property and parses it.
+ * Returns true and sets outValue if the key was found and its value was parsed successfully.
+ * Otherwise returns false and does not modify outValue. (Also logs a warning.)
+ */
+ bool tryGetProperty(const String8& key, String8& outValue) const;
+ bool tryGetProperty(const String8& key, bool& outValue) const;
+ bool tryGetProperty(const String8& key, int32_t& outValue) const;
+ bool tryGetProperty(const String8& key, float& outValue) const;
+
+ /* Adds all values from the specified property map. */
+ void addAll(const PropertyMap* map);
+
+ /* Gets the underlying property map. */
+ inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
+
+ /* Loads a property map from a file. */
+ static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename);
+
+private:
+ class Parser {
+ PropertyMap* mMap;
+ Tokenizer* mTokenizer;
+
+ public:
+ Parser(PropertyMap* map, Tokenizer* tokenizer);
+ ~Parser();
+ status_t parse();
+
+ private:
+ status_t parseType();
+ status_t parseKey();
+ status_t parseKeyProperty();
+ status_t parseModifier(const String8& token, int32_t* outMetaState);
+ status_t parseCharacterLiteral(char16_t* outCharacter);
+ };
+
+ KeyedVector<String8, String8> mProperties;
+};
+
+} // namespace android
+
+#endif // _UTILS_PROPERTY_MAP_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index ee010a3..886f1f7 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -96,7 +96,7 @@
// are included in the movement.
// The positions array contains position information for each pointer in order by
// increasing id. Its size should be equal to the number of one bits in idBits.
- void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
// Adds movement information for all pointers in a MotionEvent, including historical samples.
void addMovement(const MotionEvent* event);
@@ -149,7 +149,7 @@
virtual void clear() = 0;
virtual void clearPointers(BitSet32 idBits) = 0;
virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) = 0;
+ const std::vector<VelocityTracker::Position>& positions) = 0;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
};
@@ -180,8 +180,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
@@ -223,8 +223,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
@@ -257,8 +257,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
@@ -292,8 +292,8 @@
virtual void clear();
virtual void clearPointers(BitSet32 idBits);
- virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions);
+ void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) override;
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
new file mode 100644
index 0000000..b85aecd
--- /dev/null
+++ b/libs/attestation/Android.bp
@@ -0,0 +1,31 @@
+// 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.
+cc_library_static {
+ name: "libattestation",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "HmacKeyManager.cpp"
+ ],
+
+ clang: true,
+
+ shared_libs: [
+ "liblog",
+ "libcrypto",
+ ],
+}
\ No newline at end of file
diff --git a/libs/attestation/HmacKeyManager.cpp b/libs/attestation/HmacKeyManager.cpp
new file mode 100644
index 0000000..b15f143
--- /dev/null
+++ b/libs/attestation/HmacKeyManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <attestation/HmacKeyManager.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+
+namespace android {
+
+static std::array<uint8_t, 128> getRandomKey() {
+ std::array<uint8_t, 128> key;
+ if (RAND_bytes(key.data(), key.size()) != 1) {
+ LOG_ALWAYS_FATAL("Can't generate HMAC key");
+ }
+ return key;
+}
+
+HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
+ // SHA256 always generates 32-bytes result
+ std::array<uint8_t, 32> hash;
+ unsigned int hashLen = 0;
+ uint8_t* result =
+ HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
+ if (result == nullptr) {
+ ALOGE("Could not sign the data using HMAC");
+ return INVALID_HMAC;
+ }
+
+ if (hashLen != hash.size()) {
+ ALOGE("HMAC-SHA256 has unexpected length");
+ return INVALID_HMAC;
+ }
+
+ return hash;
+}
+} // namespace android
\ No newline at end of file
diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS
new file mode 100644
index 0000000..4dbb0ea
--- /dev/null
+++ b/libs/attestation/OWNERS
@@ -0,0 +1,2 @@
+chaviw@google.com
+svv@google.com
\ No newline at end of file
diff --git a/libs/attestation/TEST_MAPPING b/libs/attestation/TEST_MAPPING
new file mode 100644
index 0000000..43be638
--- /dev/null
+++ b/libs/attestation/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libattestation_tests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
new file mode 100644
index 0000000..6ce5ea1
--- /dev/null
+++ b/libs/attestation/tests/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+cc_test {
+ name: "libattestation_tests",
+ test_suites: ["device-tests"],
+ srcs: [
+ "HmacKeyManager_test.cpp",
+ ],
+ static_libs: [
+ "libattestation",
+ ],
+ shared_libs: [
+ "liblog",
+ "libcrypto",
+ ],
+}
diff --git a/libs/attestation/tests/HmacKeyManager_test.cpp b/libs/attestation/tests/HmacKeyManager_test.cpp
new file mode 100644
index 0000000..7f7a408
--- /dev/null
+++ b/libs/attestation/tests/HmacKeyManager_test.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <attestation/HmacKeyManager.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HmacKeyManagerTest : public testing::Test {
+protected:
+ HmacKeyManager mHmacKeyManager;
+};
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
+ std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
+
+ std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(data.data(), sizeof(data));
+ std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(data.data(), sizeof(data));
+ ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in the hmac verification data produce a different hmac.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
+ std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8};
+ std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(data.data(), sizeof(data));
+
+ data[2] = 2;
+ ASSERT_NE(initialHmac, mHmacKeyManager.sign(data.data(), sizeof(data)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index d005058..5e4c98f 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,15 +98,6 @@
return PROCESS_STATE_UNKNOWN;
}
-bool ActivityManager::setSchedPolicyCgroup(const int32_t tid, const int32_t group)
-{
- sp<IActivityManager> service = getService();
- if (service != nullptr) {
- return service->setSchedPolicyCgroup(tid, group);
- }
- return false;
-}
-
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 861b589..d363ee9 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -78,7 +78,7 @@
// or dessert updates. Instead, apex users should use libbinder_ndk.
apex_available: [
"//apex_available:platform",
- // TODO(b/139016109) remove these three
+ // TODO(b/166468760) remove these three
"com.android.media.swcodec",
"test_com.android.media.swcodec",
],
@@ -99,6 +99,7 @@
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
"Parcel.cpp",
+ "ParcelableHolder.cpp",
"ParcelFileDescriptor.cpp",
"PersistableBundle.cpp",
"ProcessState.cpp",
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index a3021122..1eb5363 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,17 +104,6 @@
}
return reply.readInt32();
}
-
- virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
- data.writeInt32(tid);
- data.writeInt32(group);
- remote()->transact(SET_SCHED_POLICY_CGROUP_TRANSACTION, data, &reply);
- if (reply.readExceptionCode() != 0) return false;
- return reply.readBool();
- }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 157538e..05fcc2b 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -448,6 +448,14 @@
return mLastTransactionBinderFlags;
}
+void IPCThreadState::setCallRestriction(ProcessState::CallRestriction restriction) {
+ mCallRestriction = restriction;
+}
+
+ProcessState::CallRestriction IPCThreadState::getCallRestriction() const {
+ return mCallRestriction;
+}
+
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
@@ -679,7 +687,7 @@
CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
ANDROID_LOG_ERROR);
} else /* FATAL_IF_NOT_ONEWAY */ {
- LOG_ALWAYS_FATAL("Process may not make oneway calls (code: %u).", code);
+ LOG_ALWAYS_FATAL("Process may not make non-oneway calls (code: %u).", code);
}
}
@@ -860,6 +868,10 @@
err = FAILED_TRANSACTION;
goto finish;
+ case BR_FROZEN_REPLY:
+ err = FAILED_TRANSACTION;
+ goto finish;
+
case BR_ACQUIRE_RESULT:
{
ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
@@ -1316,6 +1328,42 @@
}
}
+status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received)
+{
+ int ret = 0;
+ binder_frozen_status_info info;
+ info.pid = pid;
+
+#if defined(__ANDROID__)
+ if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
+ ret = -errno;
+#endif
+ *sync_received = info.sync_recv;
+ *async_received = info.async_recv;
+
+ return ret;
+}
+
+status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
+ struct binder_freeze_info info;
+ int ret = 0;
+
+ info.pid = pid;
+ info.enable = enable;
+ info.timeout_ms = timeout_ms;
+
+
+#if defined(__ANDROID__)
+ if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0)
+ ret = -errno;
+#endif
+
+ //
+ // ret==-EAGAIN indicates that transactions have not drained.
+ // Call again to poll for completion.
+ //
+ return ret;
+}
void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data,
size_t /*dataSize*/,
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9aa82d9..6d728dc 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -74,6 +74,7 @@
Vector<String16> listServices(int dumpsysPriority) override;
sp<IBinder> waitForService(const String16& name16) override;
bool isDeclared(const String16& name) override;
+ Vector<String16> getDeclaredInstances(const String16& interface) override;
// for legacy ABI
const String16& getInterfaceDescriptor() const override {
@@ -373,4 +374,18 @@
return declared;
}
+Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
+ std::vector<std::string> out;
+ if (!mTheRealServiceManager->getDeclaredInstances(String8(interface).c_str(), &out).isOk()) {
+ return {};
+ }
+
+ Vector<String16> res;
+ res.setCapacity(out.size());
+ for (const std::string& instance : out) {
+ res.push(String16(instance.c_str()));
+ }
+ return res;
+}
+
} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 14ab60f..ddd9f9b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -77,9 +77,8 @@
// many things compile this into prebuilts on the stack
static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120);
-static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
-static size_t gParcelGlobalAllocSize = 0;
-static size_t gParcelGlobalAllocCount = 0;
+static std::atomic<size_t> gParcelGlobalAllocCount;
+static std::atomic<size_t> gParcelGlobalAllocSize;
static size_t gMaxFds = 0;
@@ -275,17 +274,11 @@
}
size_t Parcel::getGlobalAllocSize() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t size = gParcelGlobalAllocSize;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return size;
+ return gParcelGlobalAllocSize.load();
}
size_t Parcel::getGlobalAllocCount() {
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- size_t count = gParcelGlobalAllocCount;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
- return count;
+ return gParcelGlobalAllocCount.load();
}
const uint8_t* Parcel::data() const
@@ -527,14 +520,19 @@
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
+ return writeInterfaceToken(interface.string(), interface.size());
+}
+
+status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ?
threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
+
// currently the interface identification token is just its name as a string
- return writeString16(interface);
+ return writeString16(str, len);
}
bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
@@ -2625,16 +2623,8 @@
releaseObjects();
if (mData) {
LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- if (mDataCapacity <= gParcelGlobalAllocSize) {
- gParcelGlobalAllocSize = gParcelGlobalAllocSize - mDataCapacity;
- } else {
- gParcelGlobalAllocSize = 0;
- }
- if (gParcelGlobalAllocCount > 0) {
- gParcelGlobalAllocCount--;
- }
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
+ gParcelGlobalAllocSize -= mDataCapacity;
+ gParcelGlobalAllocCount--;
free(mData);
}
if (mObjects) free(mObjects);
@@ -2680,13 +2670,15 @@
if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
- gParcelGlobalAllocSize += desired;
- gParcelGlobalAllocSize -= mDataCapacity;
+ if (mDataCapacity > desired) {
+ gParcelGlobalAllocSize -= (mDataCapacity - desired);
+ } else {
+ gParcelGlobalAllocSize += (desired - mDataCapacity);
+ }
+
if (!mData) {
gParcelGlobalAllocCount++;
}
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
}
@@ -2774,10 +2766,8 @@
mOwner = nullptr;
LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mObjects = objects;
@@ -2825,10 +2815,8 @@
if (data) {
LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocSize -= mDataCapacity;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataCapacity = desired;
} else {
@@ -2860,10 +2848,8 @@
}
LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
- pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
gParcelGlobalAllocSize += desired;
gParcelGlobalAllocCount++;
- pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
mData = data;
mDataSize = mDataPos = 0;
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
new file mode 100644
index 0000000..e9df279
--- /dev/null
+++ b/libs/binder/ParcelableHolder.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/ParcelableHolder.h>
+
+#define RETURN_ON_FAILURE(expr) \
+ do { \
+ android::status_t _status = (expr); \
+ if (_status != android::OK) return _status; \
+ } while (false)
+
+namespace android {
+namespace os {
+status_t ParcelableHolder::writeToParcel(Parcel* p) const {
+ std::lock_guard<std::mutex> l(mMutex);
+ RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
+ if (this->mParcelPtr) {
+ RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
+ RETURN_ON_FAILURE(p->appendFrom(this->mParcelPtr.get(), 0, this->mParcelPtr->dataSize()));
+ return OK;
+ }
+ if (this->mParcelable) {
+ size_t sizePos = p->dataPosition();
+ RETURN_ON_FAILURE(p->writeInt32(0));
+ size_t dataStartPos = p->dataPosition();
+ RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName));
+ this->mParcelable->writeToParcel(p);
+ size_t dataSize = p->dataPosition() - dataStartPos;
+
+ p->setDataPosition(sizePos);
+ RETURN_ON_FAILURE(p->writeInt32(dataSize));
+ p->setDataPosition(p->dataPosition() + dataSize);
+ return OK;
+ }
+
+ RETURN_ON_FAILURE(p->writeInt32(0));
+ return OK;
+}
+
+status_t ParcelableHolder::readFromParcel(const Parcel* p) {
+ std::lock_guard<std::mutex> l(mMutex);
+ this->mStability = static_cast<Stability>(p->readInt32());
+ this->mParcelable = nullptr;
+ this->mParcelableName = std::nullopt;
+ int32_t rawDataSize;
+
+ status_t status = p->readInt32(&rawDataSize);
+ if (status != android::OK || rawDataSize < 0) {
+ this->mParcelPtr = nullptr;
+ return status != android::OK ? status : BAD_VALUE;
+ }
+ if (rawDataSize == 0) {
+ if (this->mParcelPtr) {
+ this->mParcelPtr = nullptr;
+ }
+ return OK;
+ }
+
+ size_t dataSize = rawDataSize;
+
+ size_t dataStartPos = p->dataPosition();
+
+ if (dataStartPos > SIZE_MAX - dataSize) {
+ this->mParcelPtr = nullptr;
+ return BAD_VALUE;
+ }
+
+ if (!this->mParcelPtr) {
+ this->mParcelPtr = std::make_unique<Parcel>();
+ }
+ this->mParcelPtr->freeData();
+
+ status = this->mParcelPtr->appendFrom(p, dataStartPos, dataSize);
+ if (status != android::OK) {
+ this->mParcelPtr = nullptr;
+ return status;
+ }
+ p->setDataPosition(dataStartPos + dataSize);
+ return OK;
+}
+} // namespace os
+} // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index acc1e67..83ca687 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -144,11 +144,9 @@
}
}
-bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+bool ProcessState::becomeContextManager()
{
AutoMutex _l(mLock);
- mBinderContextCheckFunc = checkFunc;
- mBinderContextUserData = userData;
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
@@ -160,13 +158,11 @@
if (result != 0) {
android_errorWriteLog(0x534e4554, "121035042");
- int dummy = 0;
- result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+ int unused = 0;
+ result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused);
}
if (result == -1) {
- mBinderContextCheckFunc = nullptr;
- mBinderContextUserData = nullptr;
ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
}
@@ -286,9 +282,17 @@
// a driver API to get a handle to the context manager with
// proper reference counting.
+ IPCThreadState* ipc = IPCThreadState::self();
+
+ CallRestriction originalCallRestriction = ipc->getCallRestriction();
+ ipc->setCallRestriction(CallRestriction::NONE);
+
Parcel data;
- status_t status = IPCThreadState::self()->transact(
+ status_t status = ipc->transact(
0, IBinder::PING_TRANSACTION, data, nullptr, 0);
+
+ ipc->setCallRestriction(originalCallRestriction);
+
if (status == DEAD_OBJECT)
return nullptr;
}
@@ -397,14 +401,12 @@
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
- , mBinderContextCheckFunc(nullptr)
- , mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
-// TODO(b/139016109): enforce in build system
+// TODO(b/166468760): enforce in build system
#if defined(__ANDROID_APEX__)
LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ff15460..2b1e492 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -99,6 +99,14 @@
boolean isDeclared(@utf8InCpp String name);
/**
+ * Returns all declared instances for a particular interface.
+ *
+ * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+ * passed here, then ["foo"] would be returned.
+ */
+ @utf8InCpp String[] getDeclaredInstances(@utf8InCpp String iface);
+
+ /**
* Request a callback when the number of clients of the service changes.
* Used by LazyServiceRegistrar to dynamically stop services that have no clients.
*/
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
deleted file mode 100644
index 6657edb..0000000
--- a/libs/binder/fuzzer/main.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define FUZZ_LOG_TAG "main"
-
-#include "binder.h"
-#include "binder_ndk.h"
-#include "hwbinder.h"
-#include "util.h"
-
-#include <android-base/logging.h>
-
-#include <cstdlib>
-#include <ctime>
-
-template <typename P>
-void doFuzz(
- const std::vector<ParcelRead<P>>& reads,
- const std::vector<uint8_t>& input,
- const std::vector<uint8_t>& instructions) {
-
- P p;
- p.setData(input.data(), input.size());
-
- // since we are only using a byte to index
- CHECK(reads.size() <= 255) << reads.size();
-
- for (size_t i = 0; i < instructions.size() - 1; i += 2) {
- uint8_t a = instructions[i];
- uint8_t readIdx = a % reads.size();
-
- uint8_t b = instructions[i + 1];
-
- FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
- << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
- << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
- << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
- << " cap: " << p.dataCapacity();
-
- reads[readIdx](p, b);
- }
-}
-
-void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
- uint8_t parcelType = options & 0x3;
-
- switch (parcelType) {
- case 0x0:
- doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input,
- instructions);
- break;
- case 0x1:
- doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
- break;
- case 0x2:
- doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
- break;
- case 0x3:
- /*reserved for future use*/
- break;
- default:
- LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType));
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size <= 1) return 0; // no use
-
- // avoid timeouts, see b/142617274, b/142473153
- if (size > 50000) return 0;
-
- uint8_t options = *data;
- data++;
- size--;
-
- // TODO: generate 'objects' data
-
- // data to fill out parcel
- size_t inputLen = size / 2;
- std::vector<uint8_t> input(data, data + inputLen);
- data += inputLen;
- size -= inputLen;
-
- // data to use to determine what to do
- size_t instructionLen = size;
- std::vector<uint8_t> instructions(data, data + instructionLen);
- data += instructionLen;
- size -= instructionLen;
-
- CHECK(size == 0) << "size: " << size;
-
- FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
- FUZZ_LOG() << "input: " << hexString(input);
- FUZZ_LOG() << "instructions: " << hexString(instructions);
-
- fuzz(options, input, instructions);
- return 0;
-}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 7043b17..9108e31 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -77,7 +77,7 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
- bool setSchedPolicyCgroup(const int32_t tid, const int32_t group);
+
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 6d04f13..233f12a 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -133,7 +133,11 @@
OP_DEPRECATED_1 = 96,
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97,
OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98,
- _NUM_OP = 99
+ OP_NO_ISOLATED_STORAGE = 99,
+ OP_PHONE_CALL_MICROPHONE = 100,
+ OP_PHONE_CALL_CAMERA = 101,
+ OP_RECORD_AUDIO_HOTWORD = 102,
+ _NUM_OP = 103
};
AppOpsManager();
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index fe58a41..e0248f6 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,15 +39,13 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
- virtual bool setSchedPolicyCgroup(const int32_t tid, const int32_t group) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION,
- SET_SCHED_POLICY_CGROUP_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION
};
};
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index b72c854..468cc16 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -234,6 +234,7 @@
"android.gui.IGraphicBufferConsumer",
"android.gui.IRegionSamplingListener",
"android.gui.ITransactionComposerListener",
+ "android.gui.IScreenCaptureListener",
"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 2bd39a7..49ef253 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -32,9 +32,28 @@
class IPCThreadState
{
public:
+ using CallRestriction = ProcessState::CallRestriction;
+
static IPCThreadState* self();
static IPCThreadState* selfOrNull(); // self(), but won't instantiate
-
+
+ // Freeze or unfreeze the binder interface to a specific process. When freezing, this method
+ // will block up to timeout_ms to process pending transactions directed to pid. Unfreeze
+ // is immediate. Transactions to processes frozen via this method won't be delivered and the
+ // driver will return BR_FROZEN_REPLY to the client sending them. After unfreeze,
+ // transactions will be delivered normally.
+ //
+ // pid: id for the process for which the binder interface is to be frozen
+ // enable: freeze (true) or unfreeze (false)
+ // timeout_ms: maximum time this function is allowed to block the caller waiting for pending
+ // binder transactions to be processed.
+ //
+ // returns: 0 in case of success, a value < 0 in case of error
+ static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms);
+
+ // Provide information about the state of a frozen process
+ static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received,
+ bool *async_received);
sp<ProcessState> process();
status_t clearLastError();
@@ -82,6 +101,9 @@
void setLastTransactionBinderFlags(int32_t flags);
int32_t getLastTransactionBinderFlags() const;
+ void setCallRestriction(CallRestriction restriction);
+ CallRestriction getCallRestriction() const;
+
int64_t clearCallingIdentity();
// Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
@@ -140,7 +162,6 @@
// This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
// side.
static const int32_t kUnsetWorkSource = -1;
-
private:
IPCThreadState();
~IPCThreadState();
@@ -187,8 +208,7 @@
bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
-
- ProcessState::CallRestriction mCallRestriction;
+ CallRestriction mCallRestriction;
};
} // namespace android
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 1d520c1..3c5ccc1 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -96,6 +96,11 @@
* service.
*/
virtual bool isDeclared(const String16& name) = 0;
+
+ /**
+ * Get all instances of a service as declared in the VINTF manifest
+ */
+ virtual Vector<String16> getDeclaredInstances(const String16& interface) = 0;
};
sp<IServiceManager> defaultServiceManager();
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b6cfb8e..fbfd6c5 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -86,6 +86,7 @@
// Writes the RPC header.
status_t writeInterfaceToken(const String16& interface);
+ status_t writeInterfaceToken(const char16_t* str, size_t len);
// Parses the RPC header, returning true if the interface name
// in the header matches the expected interface from the caller.
diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h
index 83c2f19..a6e610c 100644
--- a/libs/binder/include/binder/Parcelable.h
+++ b/libs/binder/include/binder/Parcelable.h
@@ -56,7 +56,7 @@
// WARNING: for use by auto-generated code only (AIDL). Should not be used
// manually, or there is a risk of breaking CTS, GTS, VTS, or CTS-on-GSI
// tests.
- enum class Stability {
+ enum class Stability : int32_t {
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
new file mode 100644
index 0000000..b6814aa
--- /dev/null
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <mutex>
+#include <optional>
+#include <tuple>
+
+namespace android {
+namespace os {
+/*
+ * C++ implementation of the Java class android.os.ParcelableHolder
+ */
+class ParcelableHolder : public android::Parcelable {
+public:
+ ParcelableHolder() = delete;
+ explicit ParcelableHolder(Stability stability) : mStability(stability){};
+ virtual ~ParcelableHolder() = default;
+ ParcelableHolder(const ParcelableHolder& other) {
+ mParcelable = other.mParcelable;
+ mParcelableName = other.mParcelableName;
+ if (other.mParcelPtr) {
+ mParcelPtr = std::make_unique<Parcel>();
+ mParcelPtr->appendFrom(other.mParcelPtr.get(), 0, other.mParcelPtr->dataSize());
+ }
+ mStability = other.mStability;
+ };
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ void reset() {
+ this->mParcelable = nullptr;
+ this->mParcelableName = std::nullopt;
+ this->mParcelPtr = nullptr;
+ }
+
+ template <typename T>
+ bool setParcelable(T&& p) {
+ using Tt = typename std::decay<T>::type;
+ return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p)));
+ }
+
+ template <typename T>
+ bool setParcelable(std::shared_ptr<T> p) {
+ std::lock_guard<std::mutex> l(mMutex);
+ static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
+ if (p && this->getStability() > p->getStability()) {
+ return false;
+ }
+ this->mParcelable = p;
+ this->mParcelableName = T::getParcelableDescriptor();
+ this->mParcelPtr = nullptr;
+ return true;
+ }
+
+ template <typename T>
+ std::shared_ptr<T> getParcelable() const {
+ static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
+ std::lock_guard<std::mutex> l(mMutex);
+ const std::string& parcelableDesc = T::getParcelableDescriptor();
+ if (!this->mParcelPtr) {
+ if (!this->mParcelable || !this->mParcelableName) {
+ ALOGD("empty ParcelableHolder");
+ return nullptr;
+ } else if (parcelableDesc != *mParcelableName) {
+ ALOGD("extension class name mismatch expected:%s actual:%s",
+ mParcelableName->c_str(), parcelableDesc.c_str());
+ return nullptr;
+ }
+ return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ }
+ this->mParcelPtr->setDataPosition(0);
+ status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
+ if (status != android::OK || parcelableDesc != this->mParcelableName) {
+ this->mParcelableName = std::nullopt;
+ return nullptr;
+ }
+ this->mParcelable = std::make_shared<T>();
+ status = mParcelable.get()->readFromParcel(this->mParcelPtr.get());
+ if (status != android::OK) {
+ this->mParcelableName = std::nullopt;
+ this->mParcelable = nullptr;
+ return nullptr;
+ }
+ this->mParcelPtr = nullptr;
+ return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+ }
+
+ Stability getStability() const override { return mStability; };
+
+ inline bool operator!=(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) !=
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator<(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) <
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator<=(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) <=
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator==(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) ==
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator>(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) >
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+ inline bool operator>=(const ParcelableHolder& rhs) const {
+ return std::tie(mParcelable, mParcelPtr, mStability) >=
+ std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+ }
+
+private:
+ mutable std::shared_ptr<Parcelable> mParcelable;
+ mutable std::optional<std::string> mParcelableName;
+ mutable std::unique_ptr<Parcel> mParcelPtr;
+ Stability mStability;
+ mutable std::mutex mMutex;
+};
+} // namespace os
+} // namespace android
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9f5346a..efb95f4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,14 +50,8 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
void startThreadPool();
-
- typedef bool (*context_check_func)(const String16& name,
- const sp<IBinder>& caller,
- void* userData);
- bool becomeContextManager(
- context_check_func checkFunc,
- void* userData);
+ bool becomeContextManager();
sp<IBinder> getStrongProxyForHandle(int32_t handle);
void expungeHandle(int32_t handle, IBinder* binder);
@@ -128,9 +122,6 @@
Vector<handle_entry>mHandleToObject;
- context_check_func mBinderContextCheckFunc;
- void* mBinderContextUserData;
-
String8 mRootDir;
bool mThreadPoolStarted;
volatile int32_t mThreadPoolSeq;
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index c22be9f..7be8f7b 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -36,6 +36,60 @@
#include <sys/ioctl.h>
#include <linux/android/binder.h>
+#ifndef BR_FROZEN_REPLY
+// Temporary definition of BR_FROZEN_REPLY. For production
+// this will come from UAPI binder.h
+#define BR_FROZEN_REPLY _IO('r', 18)
+#endif //BR_FROZEN_REPLY
+
+#ifndef BINDER_FREEZE
+/*
+ * Temporary definitions for freeze support. For the final version
+ * these will be defined in the UAPI binder.h file from upstream kernel.
+ */
+#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
+
+struct binder_freeze_info {
+ //
+ // Group-leader PID of process to be frozen
+ //
+ uint32_t pid;
+ //
+ // Enable(1) / Disable(0) freeze for given PID
+ //
+ uint32_t enable;
+ //
+ // Timeout to wait for transactions to drain.
+ // 0: don't wait (ioctl will return EAGAIN if not drained)
+ // N: number of ms to wait
+ uint32_t timeout_ms;
+};
+#endif //BINDER_FREEZE
+
+#ifndef BINDER_GET_FROZEN_INFO
+
+#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
+
+struct binder_frozen_status_info {
+ //
+ // Group-leader PID of process to be queried
+ //
+ __u32 pid;
+ //
+ // Indicates whether the process has received any sync calls since last
+ // freeze (cleared at freeze/unfreeze)
+ //
+ __u32 sync_recv;
+ //
+ // Indicates whether the process has received any async calls since last
+ // freeze (cleared at freeze/unfreeze)
+ //
+ __u32 async_recv;
+};
+#endif //BINDER_GET_FROZEN_INFO
+
+
+
#ifdef __cplusplus
} // namespace android
#endif
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 7d9fd51..4610ba9 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
#include "ibinder_internal.h"
@@ -72,14 +73,13 @@
AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {}
AIBinder::~AIBinder() {}
-bool AIBinder::associateClass(const AIBinder_Class* clazz) {
- if (clazz == nullptr) return false;
+std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz,
+ const String16& newDescriptor, bool set) {
+ std::lock_guard<std::mutex> lock(mClazzMutex);
if (mClazz == clazz) return true;
- String8 newDescriptor(clazz->getInterfaceDescriptor());
-
if (mClazz != nullptr) {
- String8 currentDescriptor(mClazz->getInterfaceDescriptor());
+ const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
if (newDescriptor == currentDescriptor) {
LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
<< "' match during associateClass, but they are different class objects. "
@@ -88,33 +88,46 @@
LOG(ERROR) << __func__
<< ": Class cannot be associated on object which already has a class. "
"Trying to associate to '"
- << newDescriptor.c_str() << "' but already set to '"
- << currentDescriptor.c_str() << "'.";
+ << newDescriptor << "' but already set to '" << currentDescriptor << "'.";
}
// always a failure because we know mClazz != clazz
return false;
}
+ if (set) {
+ // if this is a local object, it's not one known to libbinder_ndk
+ mClazz = clazz;
+ return true;
+ }
+
+ return {};
+}
+
+bool AIBinder::associateClass(const AIBinder_Class* clazz) {
+ if (clazz == nullptr) return false;
+
+ const String16& newDescriptor = clazz->getInterfaceDescriptor();
+
+ auto result = associateClassInternal(clazz, newDescriptor, false);
+ if (result.has_value()) return *result;
+
CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor
- String8 descriptor(getBinder()->getInterfaceDescriptor());
+ const String16& descriptor = getBinder()->getInterfaceDescriptor();
if (descriptor != newDescriptor) {
if (getBinder()->isBinderAlive()) {
- LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
- << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+ LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
+ << "' but descriptor is actually '" << descriptor << "'.";
} else {
// b/155793159
- LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor.c_str()
+ LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
<< "' to dead binder.";
}
return false;
}
- // if this is a local object, it's not one known to libbinder_ndk
- mClazz = clazz;
-
- return true;
+ return associateClassInternal(clazz, newDescriptor, true).value();
}
ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 902fe79..0fa47c6 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -22,6 +22,7 @@
#include <atomic>
#include <mutex>
+#include <optional>
#include <vector>
#include <binder/Binder.h>
@@ -52,10 +53,14 @@
}
private:
+ std::optional<bool> associateClassInternal(const AIBinder_Class* clazz,
+ const ::android::String16& newDescriptor, bool set);
+
// AIBinder instance is instance of this class for a local object. In order to transact on a
// remote object, this also must be set for simplicity (although right now, only the
// interfaceDescriptor from it is used).
const AIBinder_Class* mClazz;
+ std::mutex mClazzMutex;
};
// This is a local AIBinder object with a known class.
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 f59bb75..18877af 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -44,9 +44,14 @@
class SpAIBinder {
public:
/**
+ * Default constructor.
+ */
+ SpAIBinder() : mBinder(nullptr) {}
+
+ /**
* Takes ownership of one strong refcount of binder.
*/
- explicit SpAIBinder(AIBinder* binder = nullptr) : mBinder(binder) {}
+ explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {}
/**
* Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not
@@ -199,6 +204,9 @@
public:
/**
* Takes ownership of a.
+ *
+ * WARNING: this constructor is only expected to be used when reading a
+ * status value. Use `ScopedAStatus::ok()` instead.
*/
explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {}
~ScopedAStatus() {}
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 f44ce0c..a4f4441 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -93,7 +93,7 @@
//
// Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
// ownership. Making this operator private to avoid double-ownership.
-#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 || defined(__ANDROID_APEX__)
private:
#else
[[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
new file mode 100644
index 0000000..6701518
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcelable_utils.h
+ * @brief Helper for parcelable.
+ */
+
+#pragma once
+
+namespace ndk {
+// Also see Parcelable.h in libbinder.
+typedef int32_t parcelable_stability_t;
+enum {
+ STABILITY_LOCAL,
+ STABILITY_VINTF, // corresponds to @VintfStability
+};
+} // namespace ndk
+
+/** @} */
diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h
new file mode 100644
index 0000000..a99d555
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_context.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+/**
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
+ *
+ * \param binder local server binder to request security contexts on
+ */
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+ __INTRODUCED_IN(31);
+
+/**
+ * Returns the selinux context of the callee.
+ *
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
+ *
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
+ */
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
+
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index d4feaba..2af65cf 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -16,40 +16,14 @@
#pragma once
+// binder_context.h used to be part of this header and is included for backwards
+// compatibility.
+#include <android/binder_context.h>
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
#include <android/binder_ibinder.h>
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
#include <binder/IBinder.h>
-#endif
-
-__BEGIN_DECLS
-
-/**
- * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
- * must be called on a local binder server before it is sent out to any othe
- * process. If this is a remote binder, it will abort. If the kernel doesn't
- * support this feature, you'll always get null from AIBinder_getCallingSid.
- *
- * \param binder local server binder to request security contexts on
- */
-void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) __INTRODUCED_IN(31);
-
-/**
- * Returns the selinux context of the callee.
- *
- * In order for this to work, the following conditions must be met:
- * - The kernel must be new enough to support this feature.
- * - The server must have called AIBinder_setRequestingSid.
- * - The callee must be a remote process.
- *
- * \return security context or null if unavailable. The lifetime of this context
- * is the lifetime of the transaction.
- */
-__attribute__((warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-
-__END_DECLS
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
/**
* Get libbinder version of binder from AIBinder.
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 52bcd20..2784aa8 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -29,9 +29,9 @@
* \param binder object to register globally with the service manager.
* \param instance identifier of the service. This will be used to lookup the service.
*
- * \return STATUS_OK on success.
+ * \return EX_NONE on success.
*/
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance);
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance);
/**
* Gets a binder object with this specific instance name. Will return nullptr immediately if the
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c33c44f..722ae23 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -272,7 +272,7 @@
}
binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) {
- return PruneStatusT(status->get()->writeToParcel(parcel->get()));
+ return PruneStatusT(status->get().writeToParcel(parcel->get()));
}
binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) {
::android::binder::Status bstatus;
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 6b2184e..c782d47 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -29,14 +29,14 @@
using ::android::status_t;
using ::android::String16;
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) {
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) {
if (binder == nullptr || instance == nullptr) {
- return STATUS_UNEXPECTED_NULL;
+ return EX_ILLEGAL_ARGUMENT;
}
sp<IServiceManager> sm = defaultServiceManager();
- status_t status = sm->addService(String16(instance), binder->getBinder());
- return PruneStatusT(status);
+ status_t exception = sm->addService(String16(instance), binder->getBinder());
+ return PruneException(exception);
}
AIBinder* AServiceManager_checkService(const char* instance) {
if (instance == nullptr) {
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 87e1341..a8ae441 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -23,7 +23,8 @@
using ::android::binder::Status;
AStatus* AStatus_newOk() {
- return new AStatus();
+ static AStatus status = AStatus();
+ return &status;
}
AStatus* AStatus_fromExceptionCode(binder_exception_t exception) {
@@ -47,27 +48,27 @@
}
bool AStatus_isOk(const AStatus* status) {
- return status->get()->isOk();
+ return status->get().isOk();
}
binder_exception_t AStatus_getExceptionCode(const AStatus* status) {
- return PruneException(status->get()->exceptionCode());
+ return PruneException(status->get().exceptionCode());
}
int32_t AStatus_getServiceSpecificError(const AStatus* status) {
- return status->get()->serviceSpecificErrorCode();
+ return status->get().serviceSpecificErrorCode();
}
binder_status_t AStatus_getStatus(const AStatus* status) {
- return PruneStatusT(status->get()->transactionError());
+ return PruneStatusT(status->get().transactionError());
}
const char* AStatus_getMessage(const AStatus* status) {
- return status->get()->exceptionMessage().c_str();
+ return status->get().exceptionMessage().c_str();
}
const char* AStatus_getDescription(const AStatus* status) {
- android::String8 description = status->get()->toString8();
+ android::String8 description = status->get().toString8();
char* cStr = new char[description.size() + 1];
memcpy(cStr, description.c_str(), description.size() + 1);
return cStr;
@@ -78,7 +79,9 @@
}
void AStatus_delete(AStatus* status) {
- delete status;
+ if (status != AStatus_newOk()) {
+ delete status;
+ }
}
binder_status_t PruneStatusT(status_t status) {
@@ -123,8 +126,8 @@
return STATUS_UNKNOWN_ERROR;
default:
- LOG(WARNING) << __func__
- << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status;
+ LOG(WARNING) << __func__ << ": Unknown status_t (" << status
+ << ") pruned into STATUS_UNKNOWN_ERROR";
return STATUS_UNKNOWN_ERROR;
}
}
@@ -155,8 +158,8 @@
return EX_TRANSACTION_FAILED;
default:
- LOG(WARNING) << __func__
- << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception;
+ LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception
+ << ") pruned into EX_TRANSACTION_FAILED";
return EX_TRANSACTION_FAILED;
}
}
diff --git a/libs/binder/ndk/status_internal.h b/libs/binder/ndk/status_internal.h
index f6227f7..cb96e48 100644
--- a/libs/binder/ndk/status_internal.h
+++ b/libs/binder/ndk/status_internal.h
@@ -25,8 +25,7 @@
AStatus() {} // ok
explicit AStatus(::android::binder::Status&& status) : mStatus(std::move(status)) {}
- ::android::binder::Status* get() { return &mStatus; }
- const ::android::binder::Status* get() const { return &mStatus; }
+ const ::android::binder::Status& get() const { return mStatus; }
private:
::android::binder::Status mStatus;
diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp
index 7c271f6..46e6270 100644
--- a/libs/binder/ndk/tests/Android.bp
+++ b/libs/binder/ndk/tests/Android.bp
@@ -66,9 +66,6 @@
],
test_suites: ["general-tests", "vts"],
require_root: true,
-
- // force since binderVendorDoubleLoadTest has its own
- auto_gen_config: true,
}
cc_test {
@@ -90,6 +87,7 @@
"libutils",
],
test_suites: ["general-tests", "vts"],
+ require_root: true,
}
aidl_interface {
diff --git a/libs/binder/ndk/tests/AndroidTest.xml b/libs/binder/ndk/tests/AndroidTest.xml
deleted file mode 100644
index 89646f7..0000000
--- a/libs/binder/ndk/tests/AndroidTest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs binderVendorDoubleLoadTest.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="binderVendorDoubleLoadTest->/data/nativetest/vendor/binderVendorDoubleLoadTest" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/nativetest/vendor" />
- <option name="module-name" value="binderVendorDoubleLoadTest" />
- </test>
-</configuration>
-
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 1424b6c..160b9f2 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -18,6 +18,7 @@
#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
+#include <android/binder_context.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
@@ -84,10 +85,11 @@
AIBinder_setRequestingSid(binder.get(), true);
- binder_status_t status = AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
+ binder_exception_t exception =
+ AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+ if (exception != EX_NONE) {
+ LOG(FATAL) << "Could not register: " << exception << " " << kBinderNdkUnitTestService;
}
ABinderProcess_joinThreadPool();
@@ -111,10 +113,10 @@
void manualService(const char* instance) {
// Strong reference to MyFoo kept by service manager.
- binder_status_t status = (new MyFoo)->addService(instance);
+ binder_exception_t exception = (new MyFoo)->addService(instance);
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << instance;
+ if (exception != EX_NONE) {
+ LOG(FATAL) << "Could not register: " << exception << " " << instance;
}
}
int manualPollingService(const char* instance) {
@@ -322,11 +324,20 @@
}
};
+TEST(NdkBinder, AddNullService) {
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
+}
+
+TEST(NdkBinder, AddInvalidServiceName) {
+ sp<IFoo> foo = new MyTestFoo;
+ EXPECT_EQ(EX_ILLEGAL_ARGUMENT, foo->addService("!@#$%^&"));
+}
+
TEST(NdkBinder, GetServiceInProcess) {
static const char* kInstanceName = "test-get-service-in-process";
sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
+ EXPECT_EQ(EX_NONE, foo->addService(kInstanceName));
sp<IFoo> getFoo = IFoo::getService(kInstanceName);
EXPECT_EQ(foo.get(), getFoo.get());
@@ -373,8 +384,8 @@
static const char* kInstanceName1 = "test-multi-1";
static const char* kInstanceName2 = "test-multi-2";
sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
+ EXPECT_EQ(EX_NONE, foo->addService(kInstanceName1));
+ EXPECT_EQ(EX_NONE, foo->addService(kInstanceName2));
EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
similarity index 95%
rename from libs/binder/fuzzer/Android.bp
rename to libs/binder/parcel_fuzzer/Android.bp
index d2b4d52..1a67898 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/parcel_fuzzer/Android.bp
@@ -12,6 +12,8 @@
"binder_ndk.cpp",
"hwbinder.cpp",
"main.cpp",
+ "random_fd.cpp",
+ "random_parcel.cpp",
"util.cpp",
],
static_libs: [
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
similarity index 94%
rename from libs/binder/fuzzer/binder.cpp
rename to libs/binder/parcel_fuzzer/binder.cpp
index 8c0495c..e5c6333 100644
--- a/libs/binder/fuzzer/binder.cpp
+++ b/libs/binder/parcel_fuzzer/binder.cpp
@@ -19,6 +19,8 @@
#include "util.h"
#include <android/os/IServiceManager.h>
+#include <binder/ParcelableHolder.h>
+#include <binder/PersistableBundle.h>
using ::android::status_t;
@@ -271,5 +273,20 @@
PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+
+ // additional parcelable objects defined in libbinder
+ [] (const ::android::Parcel& p, uint8_t data) {
+ using ::android::os::ParcelableHolder;
+ using ::android::Parcelable;
+ FUZZ_LOG() << "about to read ParcelableHolder using readParcelable with status";
+ Parcelable::Stability stability = Parcelable::Stability::STABILITY_LOCAL;
+ if ( (data & 1) == 1 ) {
+ stability = Parcelable::Stability::STABILITY_VINTF;
+ }
+ ParcelableHolder t = ParcelableHolder(stability);
+ status_t status = p.readParcelable(&t);
+ FUZZ_LOG() << "ParcelableHolder status: " << status;
+ },
+ PARCEL_READ_WITH_STATUS(android::os::PersistableBundle, readParcelable),
};
// clang-format on
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/binder.h
similarity index 98%
rename from libs/binder/fuzzer/binder.h
rename to libs/binder/parcel_fuzzer/binder.h
index b224ef4..0c51d68 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/binder.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <binder/Parcel.h>
#include <vector>
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/parcel_fuzzer/binder_ndk.cpp
similarity index 100%
rename from libs/binder/fuzzer/binder_ndk.cpp
rename to libs/binder/parcel_fuzzer/binder_ndk.cpp
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/parcel_fuzzer/binder_ndk.h
similarity index 91%
rename from libs/binder/fuzzer/binder_ndk.h
rename to libs/binder/parcel_fuzzer/binder_ndk.h
index 622cafc..e69d9c1 100644
--- a/libs/binder/fuzzer/binder_ndk.h
+++ b/libs/binder/parcel_fuzzer/binder_ndk.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <android/binder_auto_utils.h>
#include <vector>
@@ -31,6 +32,9 @@
const AParcel* aParcel() const { return mParcel.get(); }
AParcel* aParcel() { return mParcel.get(); }
+ android::Parcel* parcel() { return aParcel()->get(); }
+
+ const uint8_t* data() const { return aParcel()->get()->data(); }
size_t dataSize() const { return aParcel()->get()->dataSize(); }
size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/fuzzer/hwbinder.cpp
rename to libs/binder/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/fuzzer/hwbinder.h b/libs/binder/parcel_fuzzer/hwbinder.h
similarity index 98%
rename from libs/binder/fuzzer/hwbinder.h
rename to libs/binder/parcel_fuzzer/hwbinder.h
index a6c66be..1fa56d4 100644
--- a/libs/binder/fuzzer/hwbinder.h
+++ b/libs/binder/parcel_fuzzer/hwbinder.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <hwbinder/Parcel.h>
#include <vector>
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
new file mode 100644
index 0000000..46bf417
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "random_parcel.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdlib>
+#include <ctime>
+
+using android::fillRandomParcel;
+
+void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
+ std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
+ p->setData(input.data(), input.size());
+}
+
+template <typename P>
+void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+ FuzzedDataProvider&& provider) {
+ // Allow some majority of the bytes to be dedicated to telling us what to
+ // do. The fixed value added here represents that we want to test doing a
+ // lot of 'instructions' even on really short parcels.
+ size_t maxInstructions = 20 + (provider.remaining_bytes() * 2 / 3);
+ // but don't always use that many instructions. We want to allow the fuzzer
+ // to explore large parcels with few instructions if it wants to.
+ std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
+
+ P p;
+ fillRandomParcel(&p, std::move(provider));
+
+ // since we are only using a byte to index
+ CHECK(reads.size() <= 255) << reads.size();
+
+ FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "input: " << hexString(p.data(), p.dataSize());
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ for (size_t i = 0; i + 1 < instructions.size(); i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t readIdx = a % reads.size();
+
+ uint8_t b = instructions[i + 1];
+
+ FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+ << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
+ << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
+ << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
+ << " cap: " << p.dataCapacity();
+
+ reads[readIdx](p, b);
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ // avoid timeouts, see b/142617274, b/142473153
+ if (size > 50000) return 0;
+
+ FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+ const std::function<void(FuzzedDataProvider &&)> fuzzBackend[3] = {
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ };
+
+ provider.PickValueInArray(fuzzBackend)(std::move(provider));
+
+ return 0;
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
similarity index 97%
rename from libs/binder/fuzzer/parcel_fuzzer.h
rename to libs/binder/parcel_fuzzer/parcel_fuzzer.h
index 10cf17c..b68a8a9 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
template <typename P>
using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/parcel_fuzzer/random_fd.cpp
new file mode 100644
index 0000000..eb80ece
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_fd.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "random_fd.h"
+
+#include <fcntl.h>
+
+#include <android-base/logging.h>
+#include <cutils/ashmem.h>
+
+namespace android {
+
+int getRandomFd(FuzzedDataProvider* provider) {
+ int fd = provider->PickValueInArray<std::function<int()>>({
+ []() { return ashmem_create_region("binder test region", 1024); },
+ []() { return open("/dev/null", O_RDWR); },
+ })();
+ CHECK(fd >= 0);
+ return fd;
+}
+
+} // namespace android
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/binder/parcel_fuzzer/random_fd.h
similarity index 66%
rename from libs/gui/include/gui/GuiConfig.h
rename to libs/binder/parcel_fuzzer/random_fd.h
index 7aa5432..0a083d7 100644
--- a/libs/gui/include/gui/GuiConfig.h
+++ b/libs/binder/parcel_fuzzer/random_fd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-#ifndef ANDROID_GUI_CONFIG_H
-#define ANDROID_GUI_CONFIG_H
+#pragma once
-#include <string>
+#include <fuzzer/FuzzedDataProvider.h>
namespace android {
-// Append the libgui configuration details to configStr.
-void appendGuiConfigString(std::string& configStr);
+// ownership to callee, always valid or aborts
+// get a random FD for use in fuzzing, of a few different specific types
+int getRandomFd(FuzzedDataProvider* provider);
-}; // namespace android
-
-#endif /*ANDROID_GUI_CONFIG_H*/
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/parcel_fuzzer/random_parcel.cpp
new file mode 100644
index 0000000..3dae904
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_parcel.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "random_parcel.h"
+
+#include "random_fd.h"
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <utils/String16.h>
+
+namespace android {
+
+void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) {
+ fillRandomParcel(p->parcel(), std::move(provider));
+}
+
+class NamedBinder : public BBinder {
+public:
+ NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
+ const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+private:
+ String16 mDescriptor;
+};
+
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+ while (provider.remaining_bytes() > 0) {
+ auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
+ // write data
+ [&]() {
+ size_t toWrite =
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
+ std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(toWrite);
+ CHECK(OK == p->write(data.data(), data.size()));
+ },
+ // write FD
+ [&]() {
+ int fd = getRandomFd(&provider);
+ CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/));
+ },
+ // 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());
+ },
+ []() { return nullptr; },
+ });
+ sp<IBinder> binder = makeFunc();
+ CHECK(OK == p->writeStrongBinder(binder));
+ },
+ });
+
+ fillFunc();
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/random_parcel.h
similarity index 65%
copy from libs/binder/fuzzer/binder.h
copy to libs/binder/parcel_fuzzer/random_parcel.h
index b224ef4..2923c47 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/random_parcel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,9 +14,14 @@
* limitations under the License.
*/
+#pragma once
+
+#include "binder_ndk.h"
+
#include <binder/Parcel.h>
-#include <vector>
+#include <fuzzer/FuzzedDataProvider.h>
-#include "parcel_fuzzer.h"
-
-extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+namespace android {
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
+void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider);
+} // namespace android
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/fuzzer/util.cpp
rename to libs/binder/parcel_fuzzer/util.cpp
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/parcel_fuzzer/util.h
similarity index 98%
rename from libs/binder/fuzzer/util.h
rename to libs/binder/parcel_fuzzer/util.h
index aa504d2..45e8c57 100644
--- a/libs/binder/fuzzer/util.h
+++ b/libs/binder/parcel_fuzzer/util.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <iostream>
#include <sstream>
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 16811ee..dc8270e 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -3,12 +3,24 @@
crate_name: "binder",
srcs: ["src/lib.rs"],
shared_libs: [
- "libbinder_ndk",
"libutils",
],
rustlibs: [
"liblibc",
- "libbinder_ndk_bindgen",
+ "libbinder_ndk_sys",
+ ],
+ host_supported: true,
+}
+
+rust_library {
+ name: "libbinder_ndk_sys",
+ crate_name: "binder_ndk_sys",
+ srcs: [
+ "sys/lib.rs",
+ ":libbinder_ndk_bindgen",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
],
host_supported: true,
}
@@ -16,11 +28,8 @@
rust_bindgen {
name: "libbinder_ndk_bindgen",
crate_name: "binder_ndk_bindgen",
- wrapper_src: "BinderBindings.h",
- source_stem: "ndk_bindings",
- cflags: [
- "-x c++",
- ],
+ wrapper_src: "sys/BinderBindings.hpp",
+ source_stem: "bindings",
bindgen_flags: [
// Unfortunately the only way to specify the rust_non_exhaustive enum
// style for a type is to make it the default
@@ -69,6 +78,6 @@
],
rustlibs: [
"liblibc",
- "libbinder_ndk_bindgen",
+ "libbinder_ndk_sys",
],
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index f5e7509..6d0a369 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -61,7 +61,7 @@
/// the AIDL backend, users need only implement the high-level AIDL-defined
/// interface. The AIDL compiler then generates a container struct that wraps
/// the user-defined service and implements `Remotable`.
-pub trait Remotable: Sync {
+pub trait Remotable: Send + Sync {
/// The Binder interface descriptor string.
///
/// This string is a unique identifier for a Binder interface, and should be
@@ -98,6 +98,9 @@
/// Send a ping transaction to this object
fn ping_binder(&mut self) -> Result<()>;
+ /// Indicate that the service intends to receive caller security contexts.
+ fn set_requesting_sid(&mut self, enable: bool);
+
/// Dump this object to the given file handle
fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()>;
@@ -529,7 +532,17 @@
}
fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> {
- $on_transact(&*self.0, code, data, reply)
+ match $on_transact(&*self.0, code, data, reply) {
+ // The C++ backend converts UNEXPECTED_NULL into an exception
+ Err($crate::StatusCode::UNEXPECTED_NULL) => {
+ let status = $crate::Status::new_exception(
+ $crate::ExceptionCode::NULL_POINTER,
+ None,
+ );
+ reply.write(&status)
+ },
+ result => result
+ }
}
fn get_class() -> $crate::InterfaceClass {
@@ -581,5 +594,65 @@
parcel.write(&this.map($crate::Interface::as_binder))
}
}
+
+ impl std::fmt::Debug for dyn $interface {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.pad(stringify!($interface))
+ }
+ }
+
+ // Convert a &dyn $interface to Box<dyn $interface>
+ impl std::borrow::ToOwned for dyn $interface {
+ type Owned = Box<dyn $interface>;
+ fn to_owned(&self) -> Self::Owned {
+ self.as_binder().into_interface()
+ .expect(concat!("Error cloning interface ", stringify!($interface)))
+ }
+ }
+ };
+}
+
+/// Declare an AIDL enumeration.
+///
+/// This is mainly used internally by the AIDL compiler.
+#[macro_export]
+macro_rules! declare_binder_enum {
+ {
+ $enum:ident : $backing:ty {
+ $( $name:ident = $value:expr, )*
+ }
+ } => {
+ #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+ pub struct $enum(pub $backing);
+ impl $enum {
+ $( pub const $name: Self = Self($value); )*
+ }
+
+ impl $crate::parcel::Serialize for $enum {
+ fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ parcel.write(&self.0)
+ }
+ }
+
+ impl $crate::parcel::SerializeArray for $enum {
+ fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
+ <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
+ }
+ }
+
+ impl $crate::parcel::Deserialize for $enum {
+ fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> {
+ parcel.read().map(Self)
+ }
+ }
+
+ impl $crate::parcel::DeserializeArray for $enum {
+ fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> {
+ let v: Option<Vec<$backing>> =
+ <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
+ Ok(v.map(|v| v.into_iter().map(Self).collect()))
+ }
+ }
};
}
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index 289b157..4492cf7 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -43,14 +43,6 @@
}
}
-// impl Display for StatusCode {
-// fn fmt(&self, f: &mut Formatter) -> FmtResult {
-// write!(f, "StatusCode::{:?}", self)
-// }
-// }
-
-// impl error::Error for StatusCode {}
-
fn parse_status_code(code: i32) -> StatusCode {
match code {
e if e == StatusCode::OK as i32 => StatusCode::OK,
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 4b9cccf..8ee6a62 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -102,7 +102,7 @@
mod native;
mod state;
-use binder_ndk_bindgen as sys;
+use binder_ndk_sys as sys;
pub mod parcel;
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 798fed8..185645e 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -36,6 +36,18 @@
rust_object: *mut T,
}
+/// # Safety
+///
+/// A `Binder<T>` is a pair of unique owning pointers to two values:
+/// * a C++ ABBinder which the C++ API guarantees can be passed between threads
+/// * a Rust object which implements `Remotable`; this trait requires `Send + Sync`
+///
+/// Both pointers are unique (never escape the `Binder<T>` object and are not copied)
+/// so we can essentially treat `Binder<T>` as a box-like containing the two objects;
+/// the box-like object inherits `Send` from the two inner values, similarly
+/// to how `Box<T>` is `Send` if `T` is `Send`.
+unsafe impl<T: Remotable> Send for Binder<T> {}
+
impl<T: Remotable> Binder<T> {
/// Create a new Binder remotable object.
///
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 43850fe..2c1e5a4 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -21,6 +21,7 @@
use crate::proxy::SpIBinder;
use crate::sys;
+use std::cell::RefCell;
use std::convert::TryInto;
use std::mem::ManuallyDrop;
use std::ptr;
@@ -117,6 +118,55 @@
}
}
+ /// Perform a series of writes to the `Parcel`, prepended with the length
+ /// (in bytes) of the written data.
+ ///
+ /// The length `0i32` will be written to the parcel first, followed by the
+ /// writes performed by the callback. The initial length will then be
+ /// updated to the length of all data written by the callback, plus the
+ /// size of the length elemement itself (4 bytes).
+ ///
+ /// # Examples
+ ///
+ /// After the following call:
+ ///
+ /// ```
+ /// # use binder::{Binder, Interface, Parcel};
+ /// # let mut parcel = Parcel::Owned(std::ptr::null_mut());
+ /// parcel.sized_write(|subparcel| {
+ /// subparcel.write(&1u32)?;
+ /// subparcel.write(&2u32)?;
+ /// subparcel.write(&3u32)
+ /// });
+ /// ```
+ ///
+ /// `parcel` will contain the following:
+ ///
+ /// ```ignore
+ /// [16i32, 1u32, 2u32, 3u32]
+ /// ```
+ pub fn sized_write<F>(&mut self, f: F) -> Result<()>
+ where for<'a>
+ F: Fn(&'a WritableSubParcel<'a>) -> Result<()>
+ {
+ let start = self.get_data_position();
+ self.write(&0i32)?;
+ {
+ let subparcel = WritableSubParcel(RefCell::new(self));
+ f(&subparcel)?;
+ }
+ let end = self.get_data_position();
+ unsafe {
+ self.set_data_position(start)?;
+ }
+ assert!(end >= start);
+ self.write(&(end - start))?;
+ unsafe {
+ self.set_data_position(end)?;
+ }
+ Ok(())
+ }
+
/// Returns the current position in the parcel data.
pub fn get_data_position(&self) -> i32 {
unsafe {
@@ -143,6 +193,16 @@
}
}
+/// A segment of a writable parcel, used for [`Parcel::sized_write`].
+pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>);
+
+impl<'a> WritableSubParcel<'a> {
+ /// Write a type that implements [`Serialize`] to the sub-parcel.
+ pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> {
+ parcelable.serialize(&mut *self.0.borrow_mut())
+ }
+}
+
// Data deserialization methods
impl Parcel {
/// Attempt to read a type that implements [`Deserialize`] from this
@@ -285,7 +345,6 @@
fn test_read_write() {
use crate::binder::Interface;
use crate::native::Binder;
- use std::ffi::CString;
let mut service = Binder::new(()).as_binder();
let mut parcel = Parcel::new_for_test(&mut service).unwrap();
@@ -300,7 +359,7 @@
assert_eq!(parcel.read::<u64>(), Err(StatusCode::NOT_ENOUGH_DATA));
assert_eq!(parcel.read::<f32>(), Err(StatusCode::NOT_ENOUGH_DATA));
assert_eq!(parcel.read::<f64>(), Err(StatusCode::NOT_ENOUGH_DATA));
- assert_eq!(parcel.read::<Option<CString>>(), Ok(None));
+ assert_eq!(parcel.read::<Option<String>>(), Ok(None));
assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL));
assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE));
@@ -410,11 +469,24 @@
}
assert_eq!(
parcel.read::<Option<String>>().unwrap().unwrap(),
- "Hello, Binder!"
+ "Hello, Binder!",
);
unsafe {
assert!(parcel.set_data_position(start).is_ok());
}
+
+ assert!(parcel.write("Embedded null \0 inside a string").is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert_eq!(
+ parcel.read::<Option<String>>().unwrap().unwrap(),
+ "Embedded null \0 inside a string",
+ );
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok());
assert!(parcel
.write(
@@ -445,3 +517,38 @@
);
assert_eq!(parcel.read::<Vec<String>>().unwrap(), [s1, s2, s3]);
}
+
+#[test]
+fn test_sized_write() {
+ use crate::binder::Interface;
+ use crate::native::Binder;
+
+ let mut service = Binder::new(()).as_binder();
+ let mut parcel = Parcel::new_for_test(&mut service).unwrap();
+ let start = parcel.get_data_position();
+
+ let arr = [1i32, 2i32, 3i32];
+
+ parcel.sized_write(|subparcel| {
+ subparcel.write(&arr[..])
+ }).expect("Could not perform sized write");
+
+ // i32 sub-parcel length + i32 array length + 3 i32 elements
+ let expected_len = 20i32;
+
+ assert_eq!(parcel.get_data_position(), start + expected_len);
+
+ unsafe {
+ parcel.set_data_position(start).unwrap();
+ }
+
+ assert_eq!(
+ expected_len,
+ parcel.read().unwrap(),
+ );
+
+ assert_eq!(
+ parcel.read::<Vec<i32>>().unwrap(),
+ &arr,
+ );
+}
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 8a89ab0..20e9178 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -26,6 +26,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
/// Rust version of the Java class android.os.ParcelFileDescriptor
+#[derive(Debug)]
pub struct ParcelFileDescriptor(File);
impl ParcelFileDescriptor {
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 78b3d2c..138b360 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -21,7 +21,8 @@
use crate::sys;
use std::convert::TryInto;
-use std::ffi::{c_void, CStr, CString};
+use std::ffi::c_void;
+use std::os::raw::c_char;
use std::ptr;
/// A struct whose instances can be written to a [`Parcel`].
@@ -340,7 +341,7 @@
}
}
-impl SerializeOption for CStr {
+impl SerializeOption for str {
fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
match this {
None => unsafe {
@@ -356,14 +357,17 @@
},
Some(s) => unsafe {
// Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. `AParcel_writeString` assumes that we pass a
- // null-terminated C string pointer with no nulls in the middle
- // of the string. Rust guarantees exactly that for a valid CStr
- // instance.
+ // `AParcel`. `AParcel_writeString` assumes that we pass a utf-8
+ // string pointer of `length` bytes, which is what str in Rust
+ // is. The docstring for `AParcel_writeString` says that the
+ // string input should be null-terminated, but it doesn't
+ // actually rely on that fact in the code. If this ever becomes
+ // necessary, we will need to null-terminate the str buffer
+ // before sending it.
status_result(sys::AParcel_writeString(
parcel.as_native_mut(),
- s.as_ptr(),
- s.to_bytes()
+ s.as_ptr() as *const c_char,
+ s.as_bytes()
.len()
.try_into()
.or(Err(StatusCode::BAD_VALUE))?,
@@ -373,29 +377,15 @@
}
}
-impl SerializeArray for Option<&CStr> {}
+impl SerializeArray for Option<&str> {}
-impl Serialize for CStr {
+impl Serialize for str {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
Some(self).serialize(parcel)
}
}
-impl Serialize for CString {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
- Some(self.as_c_str()).serialize(parcel)
- }
-}
-
-impl SerializeArray for CString {}
-
-impl SerializeOption for CString {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
- SerializeOption::serialize_option(this.map(CString::as_c_str), parcel)
- }
-}
-
-impl SerializeArray for Option<CString> {}
+impl SerializeArray for &str {}
impl Serialize for String {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
@@ -413,7 +403,7 @@
impl SerializeArray for Option<String> {}
-impl Deserialize for Option<CString> {
+impl Deserialize for Option<String> {
fn deserialize(parcel: &Parcel) -> Result<Self> {
let mut vec: Option<Vec<u8>> = None;
let status = unsafe {
@@ -430,26 +420,15 @@
status_result(status)?;
vec.map(|mut s| {
- // The vector includes a null-terminator and CString::new requires
- // no nulls, including terminating.
+ // The vector includes a null-terminator and we don't want the
+ // string to be null-terminated for Rust.
s.pop();
- CString::new(s).or(Err(StatusCode::BAD_VALUE))
+ String::from_utf8(s).or(Err(StatusCode::BAD_VALUE))
})
.transpose()
}
}
-impl DeserializeArray for Option<CString> {}
-
-impl DeserializeOption for String {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
- let c_str = <Option<CString>>::deserialize(parcel)?;
- c_str
- .map(|s| s.into_string().or(Err(StatusCode::BAD_VALUE)))
- .transpose()
- }
-}
-
impl DeserializeArray for Option<String> {}
impl Deserialize for String {
@@ -462,28 +441,6 @@
impl DeserializeArray for String {}
-impl SerializeOption for str {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
- match this {
- None => parcel.write(&-1i32),
- Some(s) => {
- let c_str = CString::new(s).or(Err(StatusCode::BAD_VALUE))?;
- parcel.write(&c_str)
- }
- }
- }
-}
-
-impl Serialize for str {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
- Some(self).serialize(parcel)
- }
-}
-
-impl SerializeArray for &str {}
-
-impl SerializeArray for Option<&str> {}
-
impl<T: SerializeArray> Serialize for [T] {
fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
SerializeArray::serialize_array(self, parcel)
@@ -905,8 +862,9 @@
let s1 = "Hello, Binder!";
let s2 = "This is a utf8 string.";
let s3 = "Some more text here.";
+ let s4 = "Embedded nulls \0 \0";
- let strs = [s1, s2, s3];
+ let strs = [s1, s2, s3, s4];
unsafe {
assert!(parcel.set_data_position(start).is_ok());
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index f9519b4..5002fc6 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -28,6 +28,7 @@
use std::convert::TryInto;
use std::ffi::{c_void, CString};
+use std::fmt;
use std::os::unix::io::AsRawFd;
use std::ptr;
@@ -37,6 +38,12 @@
/// is untyped; typed interface access is implemented by the AIDL compiler.
pub struct SpIBinder(*mut sys::AIBinder);
+impl fmt::Debug for SpIBinder {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("SpIBinder")
+ }
+}
+
/// # Safety
///
/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe
@@ -122,6 +129,14 @@
}
}
+impl PartialEq for SpIBinder {
+ fn eq(&self, other: &Self) -> bool {
+ ptr::eq(self.0, other.0)
+ }
+}
+
+impl Eq for SpIBinder {}
+
impl Clone for SpIBinder {
fn clone(&self) -> Self {
unsafe {
@@ -234,6 +249,12 @@
status_result(status)
}
+ fn set_requesting_sid(&mut self, enable: bool) {
+ unsafe {
+ sys::AIBinder_setRequestingSid(self.as_native_mut(), enable)
+ };
+ }
+
fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> {
let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect();
let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
@@ -331,7 +352,10 @@
impl Deserialize for SpIBinder {
fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
- parcel.read_binder().transpose().unwrap()
+ parcel
+ .read_binder()
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
}
}
@@ -363,6 +387,18 @@
assert!(!ptr.is_null());
Self(ptr)
}
+
+ /// Promote this weak reference to a strong reference to the binder object.
+ pub fn promote(&self) -> Option<SpIBinder> {
+ unsafe {
+ // Safety: `WpIBinder` always contains a valid weak reference, so we
+ // can pass this pointer to `AIBinder_Weak_promote`. Returns either
+ // null or an AIBinder owned by the caller, both of which are valid
+ // to pass to `SpIBinder::from_raw`.
+ let ptr = sys::AIBinder_Weak_promote(self.0);
+ SpIBinder::from_raw(ptr)
+ }
+ }
}
/// Rust wrapper around DeathRecipient objects.
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 992f074..0e05f10 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -98,4 +98,36 @@
sys::AIBinder_getCallingPid()
}
}
+
+ /// This function makes the client's security context available to the
+ /// service calling this function. This can be used for access control.
+ /// It does not suffer from the TOCTOU issues of get_calling_pid.
+ ///
+ /// Implementations of `check_permission` should use the given CStr
+ /// argument as context for selinux permission checks. If `None` is
+ /// given, the implementation should fall back to using the PID
+ /// instead.
+ ///
+ /// Note: `None` may be passed to the callback if the caller did not
+ /// `set_requesting_sid` on the serviced binder, or if the underlying
+ /// kernel is too old to support this feature.
+ pub fn with_calling_sid<T, F>(check_permission: F) -> T
+ where
+ for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T {
+ // Safety: AIBinder_getCallingSid returns a c-string pointer
+ // that is valid for a transaction. Also, the string returned
+ // is thread local. By restricting the lifetime of the CStr
+ // reference to the scope of the callback, we prevent it being
+ // used beyond the guaranteed lifetime.
+ check_permission(unsafe {
+ let sid = sys::AIBinder_getCallingSid();
+ // AIBinder_getCallingSid() returns a '\0' terminated string
+ // or NULL.
+ if sid.is_null() {
+ None
+ } else {
+ Some(std::ffi::CStr::from_ptr(sid))
+ }
+ })
+ }
}
diff --git a/libs/binder/rust/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.hpp
similarity index 97%
rename from libs/binder/rust/BinderBindings.h
rename to libs/binder/rust/sys/BinderBindings.hpp
index c7a06d9..303f4a5 100644
--- a/libs/binder/rust/BinderBindings.h
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
new file mode 100644
index 0000000..9095af2
--- /dev/null
+++ b/libs/binder/rust/sys/lib.rs
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+//! Generated Rust bindings to libbinder_ndk
+
+#![allow(
+ non_camel_case_types,
+ non_snake_case,
+ non_upper_case_globals,
+ unused,
+ improper_ctypes,
+ missing_docs
+)]
+use std::error::Error;
+use std::fmt;
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+
+impl Error for android_c_interface_StatusCode {}
+
+impl fmt::Display for android_c_interface_StatusCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "StatusCode::{:?}", self)
+ }
+}
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index 622604f..3db40ba 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -3,6 +3,10 @@
srcs: ["integration.rs"],
rustlibs: [
"libbinder_rs",
+ "libselinux_bindgen",
+ ],
+ shared_libs: [
+ "libselinux",
],
// For the binaries to be pushed properly as specified in AndroidTest.xml,
// this cannot be the same as the module name.
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index fe59416..953d328 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -18,7 +18,8 @@
use binder::declare_binder_interface;
use binder::parcel::Parcel;
-use binder::{Binder, IBinder, Interface, SpIBinder, TransactionCode};
+use binder::{Binder, IBinder, Interface, SpIBinder, StatusCode, ThreadState, TransactionCode};
+use std::convert::{TryFrom, TryInto};
/// Name of service runner.
///
@@ -49,6 +50,7 @@
let mut service = Binder::new(BnTest(Box::new(TestService {
s: service_name.clone(),
})));
+ service.set_requesting_sid(true);
if let Some(extension_name) = extension_name {
let extension = BnTest::new_binder(TestService { s: extension_name });
service
@@ -79,18 +81,47 @@
s: String,
}
+#[repr(u32)]
+enum TestTransactionCode {
+ Test = SpIBinder::FIRST_CALL_TRANSACTION,
+ GetSelinuxContext,
+}
+
+impl TryFrom<u32> for TestTransactionCode {
+ type Error = StatusCode;
+
+ fn try_from(c: u32) -> Result<Self, Self::Error> {
+ match c {
+ _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test),
+ _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
+ Ok(TestTransactionCode::GetSelinuxContext)
+ }
+ _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+ }
+ }
+}
+
impl Interface for TestService {}
impl ITest for TestService {
fn test(&self) -> binder::Result<String> {
Ok(self.s.clone())
}
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ let sid =
+ ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
+ sid.ok_or(StatusCode::UNEXPECTED_NULL)
+ }
}
/// Trivial testing binder interface
pub trait ITest: Interface {
/// Returns a test string
fn test(&self) -> binder::Result<String>;
+
+ /// Returns the caller's SELinux context
+ fn get_selinux_context(&self) -> binder::Result<String>;
}
declare_binder_interface! {
@@ -104,19 +135,30 @@
fn on_transact(
service: &dyn ITest,
- _code: TransactionCode,
+ code: TransactionCode,
_data: &Parcel,
reply: &mut Parcel,
) -> binder::Result<()> {
- reply.write(&service.test()?)?;
- Ok(())
+ match code.try_into()? {
+ TestTransactionCode::Test => reply.write(&service.test()?),
+ TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
+ }
}
impl ITest for BpTest {
fn test(&self) -> binder::Result<String> {
- let reply = self
- .binder
- .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+ let reply =
+ self.binder
+ .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
+ reply.read()
+ }
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ let reply = self.binder.transact(
+ TestTransactionCode::GetSelinuxContext as TransactionCode,
+ 0,
+ |_| Ok(()),
+ )?;
reply.read()
}
}
@@ -125,12 +167,19 @@
fn test(&self) -> binder::Result<String> {
self.0.test()
}
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ self.0.get_selinux_context()
+ }
}
#[cfg(test)]
mod tests {
+ use selinux_bindgen as selinux_sys;
+ use std::ffi::CStr;
use std::fs::File;
use std::process::{Child, Command};
+ use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
@@ -203,6 +252,24 @@
assert_eq!(test_client.test().unwrap(), "trivial_client_test");
}
+ #[test]
+ fn get_selinux_context() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Box<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ let expected_context = unsafe {
+ let mut out_ptr = ptr::null_mut();
+ assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+ assert!(!out_ptr.is_null());
+ CStr::from_ptr(out_ptr)
+ };
+ assert_eq!(
+ test_client.get_selinux_context().unwrap(),
+ expected_context.to_str().expect("context was invalid UTF-8"),
+ );
+ }
+
fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
let binder_died = Arc::new(AtomicBool::new(false));
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 917751e..98f0868 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <fstream>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
@@ -79,6 +80,8 @@
BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
+ BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
+ BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_REJECT_BUF,
};
@@ -399,6 +402,49 @@
EXPECT_EQ(NO_ERROR, ret);
}
+TEST_F(BinderLibTest, Freeze) {
+ status_t ret;
+ Parcel data, reply, replypid;
+ std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze");
+
+ //Pass test on devices where the freezer is not supported
+ if (freezer_file.fail()) {
+ GTEST_SKIP();
+ return;
+ }
+
+ std::string freezer_enabled;
+ std::getline(freezer_file, freezer_enabled);
+
+ //Pass test on devices where the freezer is disabled
+ if (freezer_enabled != "1") {
+ GTEST_SKIP();
+ return;
+ }
+
+ ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid);
+ int32_t pid = replypid.readInt32();
+ EXPECT_EQ(NO_ERROR, ret);
+ for (int i = 0; i < 10; i++) {
+ EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
+ }
+ EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
+ EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0));
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000));
+ EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
+
+ bool sync_received, async_received;
+
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received,
+ &async_received));
+
+ EXPECT_EQ(sync_received, 1);
+ EXPECT_EQ(async_received, 0);
+
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0));
+ EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
+}
+
TEST_F(BinderLibTest, SetError) {
int32_t testValue[] = { 0, -123, 123 };
for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) {
@@ -1178,6 +1224,12 @@
pthread_mutex_unlock(&m_serverWaitMutex);
return ret;
}
+ case BINDER_LIB_TEST_GETPID:
+ reply->writeInt32(getpid());
+ return NO_ERROR;
+ case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT:
+ usleep(5000);
+ return NO_ERROR;
case BINDER_LIB_TEST_NOP_TRANSACTION:
return NO_ERROR;
case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..46379fc
--- /dev/null
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -0,0 +1,66 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "binder_fuzz_defaults",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libbase",
+ ],
+}
+
+cc_fuzz {
+ name: "binder_binderFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["BinderFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_bpBinderFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ host_supported: false,
+ srcs: ["BpBinderFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_persistableBundleFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["PersistableBundleFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_stabilityFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["StabilityFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_statusFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["StatusFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_textOutputFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["TextOutputFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/fuzzers/BinderFuzz.cpp
new file mode 100644
index 0000000..1e5d80a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzz.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <BinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ sp<BBinder> bbinder = new BBinder();
+
+ // To prevent memory from running out from calling too many add item operations.
+ const uint32_t MAX_RUNS = 2048;
+ uint32_t count = 0;
+
+ while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+ if (fdp.ConsumeBool()) {
+ callArbitraryFunction(&fdp, gBBinderOperations, bbinder);
+ } else {
+ callArbitraryFunction(&fdp, gIBinderOperations,
+ reinterpret_cast<IBinder *>(bbinder.get()));
+ }
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
new file mode 100644
index 0000000..9ac65bb
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
@@ -0,0 +1,70 @@
+/*
+ * 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 <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <stdint.h>
+#include <atomic>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BBinder>&)>>
+ gBBinderOperations = {[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->isRequestingSid();
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ bool request_sid = fdp->ConsumeBool();
+ bbinder->setRequestingSid(request_sid);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getExtension();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ static IBinder* extension = nullptr;
+ bbinder->setExtension(extension);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ int priority;
+ int policy = fdp->ConsumeIntegralInRange<int>(0, 2);
+ if (policy == 0) {
+ priority = fdp->ConsumeIntegralInRange<int>(-20, 19);
+ } else {
+ priority = fdp->ConsumeIntegralInRange<int>(1, 99);
+ }
+ bbinder->setMinSchedulerPolicy(policy, priority);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getMinSchedulerPolicy();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getMinSchedulerPriority();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getDebugPid();
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
new file mode 100644
index 0000000..c50279b
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <BpBinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ // TODO: In the future it would be more effective to fork a new process and then pass a BBinder
+ // to your process. Right now this is not implemented because it would involved fuzzing IPC on a
+ // forked process, and libfuzzer will not be able to handle code coverage. This would lead to
+ // crashes that are not easy to diagnose.
+ int32_t handle = fdp.ConsumeIntegralInRange<int32_t>(0, 1024);
+ sp<BpBinder> bpbinder = BpBinder::create(handle);
+ if (bpbinder == nullptr) return 0;
+
+ // To prevent memory from running out from calling too many add item operations.
+ const uint32_t MAX_RUNS = 2048;
+ uint32_t count = 0;
+ sp<IBinder::DeathRecipient> s_recipient = new FuzzDeathRecipient();
+
+ while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+ if (fdp.ConsumeBool()) {
+ callArbitraryFunction(&fdp, gBPBinderOperations, bpbinder, s_recipient);
+ } else {
+ callArbitraryFunction(&fdp, gIBinderOperations, bpbinder.get());
+ }
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
new file mode 100644
index 0000000..c685b41
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -0,0 +1,110 @@
+/*
+ * 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 <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// Static variable to reference so we don't consume a bunch of memory to link and
+// unlink DeathRecipients.
+static int8_t kBpBinderCookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
+ const sp<IBinder::DeathRecipient>&)>>
+ gBPBinderOperations =
+ {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>& s_recipient) -> void {
+ // Clean up possible leftover memory.
+ wp<IBinder::DeathRecipient> outRecipient(nullptr);
+ bpbinder->sendObituary();
+ bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&kBpBinderCookie), 0,
+ &outRecipient);
+
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ kBpBinderCookie = fdp->ConsumeIntegral<int8_t>();
+ bpbinder->linkToDeath(s_recipient.get(),
+ reinterpret_cast<void*>(&kBpBinderCookie), flags);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ wp<IBinder::DeathRecipient> out_recipient(nullptr);
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ int8_t random_cookie = fdp->ConsumeIntegral<int8_t>();
+ bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&random_cookie),
+ flags, &out_recipient);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->remoteBinder(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->sendObituary(); },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ uint32_t uid = fdp->ConsumeIntegral<uint32_t>();
+ bpbinder->getBinderProxyCount(uid);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->enableCountByUid(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->disableCountByUid(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ Vector<uint32_t> uids;
+ Vector<uint32_t> counts;
+ bpbinder->getCountByUid(uids, counts);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ bool enable = fdp->ConsumeBool();
+ bpbinder->setCountByUidEnabled(enable);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ binder_proxy_limit_callback cb = binder_proxy_limit_callback();
+ bpbinder->setLimitCallback(cb);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ int high = fdp->ConsumeIntegral<int>();
+ int low = fdp->ConsumeIntegral<int>();
+ bpbinder->setBinderProxyCountWatermarks(high, low);
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
new file mode 100644
index 0000000..626b758
--- /dev/null
+++ b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
@@ -0,0 +1,87 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FuzzDeathRecipient : public IBinder::DeathRecipient {
+private:
+ virtual void binderDied(const wp<IBinder>& who) { (void)who; };
+};
+
+// Allow objects to be attached that aren't stack locals
+static uint32_t objectID = 0;
+static uint32_t object = 0;
+static uint32_t cleanup_cookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, IBinder*)>> gIBinderOperations =
+ {[](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->getInterfaceDescriptor(); },
+ [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->isBinderAlive(); },
+ [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->pingBinder(); },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ int fd = STDOUT_FILENO;
+ std::string rand_str = fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ Vector<String16> args;
+ args.push(String16(rand_str.c_str()));
+ ibinder->dump(fd, args);
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ objectID = fdp->ConsumeIntegral<uint32_t>();
+ object = fdp->ConsumeIntegral<uint32_t>();
+ cleanup_cookie = fdp->ConsumeIntegral<uint32_t>();
+ IBinder::object_cleanup_func func = IBinder::object_cleanup_func();
+ ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
+ : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&object) : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&cleanup_cookie)
+ : nullptr,
+ func);
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->findObject(reinterpret_cast<void*>(&id));
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->detachObject(reinterpret_cast<void*>(&id));
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t code = fdp->ConsumeIntegral<uint32_t>();
+ Parcel p_data;
+ Parcel reply;
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->transact(code, p_data, &reply, flags);
+ }};
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
new file mode 100644
index 0000000..4843c46
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <PersistableBundleFuzzFunctions.h>
+#include <binder/PersistableBundle.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ std::shared_ptr<os::PersistableBundle> p_bundle(new os::PersistableBundle());
+
+ while (fdp.remaining_bytes() > 0) {
+ String16 key(fdp.ConsumeRandomLengthString(fdp.remaining_bytes()).c_str());
+ callArbitraryFunction(&fdp, gPersistableBundleOperations, p_bundle, &key);
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
new file mode 100644
index 0000000..820e9e8
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
@@ -0,0 +1,183 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<
+ void(FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const&, String16*)>>
+ gPersistableBundleOperations =
+ {[](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void { p_bundle->empty(); },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ Parcel parcel;
+ p_bundle->writeToParcel(&parcel);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ Parcel parcel;
+ std::vector<uint8_t> buf = fdp->ConsumeBytes<uint8_t>(
+ fdp->ConsumeIntegralInRange<size_t>(0, fdp->remaining_bytes() - 1));
+ parcel.write(buf.data(), buf.size());
+ p_bundle->readFromParcel(&parcel);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void { p_bundle->size(); },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void { p_bundle->erase(*key); },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ bool value = fdp->ConsumeBool();
+ p_bundle->putBoolean(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int32_t value = fdp->ConsumeIntegral<int32_t>();
+ p_bundle->putInt(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ os::PersistableBundle value = os::PersistableBundle();
+ p_bundle->putPersistableBundle(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<String16> value;
+ p_bundle->putStringVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<double> value;
+ p_bundle->putDoubleVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int64_t> value;
+ p_bundle->putLongVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int32_t> value;
+ p_bundle->putIntVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<bool> value;
+ p_bundle->putBooleanVector(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ String16 value(fdp->ConsumeRandomLengthString(fdp->remaining_bytes()).c_str());
+ p_bundle->putString(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int64_t value = fdp->ConsumeIntegral<int64_t>();
+ p_bundle->putLong(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ double value = fdp->ConsumeFloatingPoint<double>();
+ p_bundle->putDouble(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ bool out;
+ p_bundle->getBoolean(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ os::PersistableBundle out;
+ p_bundle->getPersistableBundle(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<String16> out;
+ p_bundle->getStringVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<double> out;
+ p_bundle->getDoubleVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int64_t> out;
+ p_bundle->getLongVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int32_t> out;
+ p_bundle->getIntVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<bool> out;
+ p_bundle->getBooleanVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ String16 out;
+ p_bundle->getString(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ double out;
+ p_bundle->getDouble(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int64_t out;
+ p_bundle->getLong(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int32_t out;
+ p_bundle->getInt(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ p_bundle->getBooleanKeys();
+ p_bundle->getIntKeys();
+ p_bundle->getLongKeys();
+ p_bundle->getDoubleKeys();
+ p_bundle->getStringKeys();
+ p_bundle->getBooleanVectorKeys();
+ p_bundle->getIntVectorKeys();
+ p_bundle->getLongVectorKeys();
+ p_bundle->getDoubleVectorKeys();
+ p_bundle->getStringVectorKeys();
+ p_bundle->getPersistableBundleKeys();
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
new file mode 100644
index 0000000..8ad9b44
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 <StabilityFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ // Init our wrapper
+ FuzzedDataProvider dataProvider(data, size);
+ android::sp<android::IBinder> bbinder = new android::BBinder();
+
+ // Call some functions
+ while (dataProvider.remaining_bytes() > 0) {
+ callArbitraryFunction(&dataProvider, gStabilityOperations, bbinder);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
new file mode 100644
index 0000000..8b4ed70
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
@@ -0,0 +1,67 @@
+/*
+ * 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 <binder/Binder.h>
+#include <binder/Stability.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#define STABILITY_MAX_TAG_LENGTH 2048
+static bool marked = false;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<
+ std::function<void(FuzzedDataProvider*, android::sp<android::IBinder> const&)>>
+ gStabilityOperations = {
+ // markCompilationUnit(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markCompilationUnit(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // markVintf(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markVintf(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // debugLogStability(const std::string& tag, const sp<IBinder>& binder)
+ [](FuzzedDataProvider* fdp, android::sp<android::IBinder> const& bbinder) -> void {
+ std::string tag = fdp->ConsumeRandomLengthString(STABILITY_MAX_TAG_LENGTH);
+ android::internal::Stability::debugLogStability(tag, bbinder);
+ },
+
+ // markVndk(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markVndk(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // requiresVintfDeclaration(const sp<IBinder>& binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ android::internal::Stability::requiresVintfDeclaration(bbinder);
+ }};
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/fuzzers/StatusFuzz.cpp
new file mode 100644
index 0000000..4f6ad6f
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzz.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 <StatusFuzzFunctions.h>
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ int32_t exceptionCode = fdp.ConsumeIntegral<int32_t>();
+ std::string message_str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ String8 message(message_str.c_str());
+
+ Parcel parcel;
+ std::vector<uint8_t> buf = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes() - 1));
+ parcel.write(buf.data(), buf.size());
+ binder::Status status = binder::Status::fromExceptionCode(exceptionCode, message);
+
+ while (fdp.remaining_bytes() > 0) {
+ callArbitraryFunction(&fdp, gStatusOperations, &status, &parcel);
+ }
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
new file mode 100644
index 0000000..bc8d17a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <stdio.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, binder::Status*, Parcel*)>>
+ gStatusOperations = {
+ [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+ parcel->setDataPosition(0);
+ status->readFromParcel(*parcel);
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+ status->writeToParcel(parcel);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ status->setServiceSpecificError(fdp->ConsumeIntegral<int32_t>(), message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ status->setException(fdp->ConsumeIntegral<int32_t>(), message);
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void { status->ok(); },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ *status = binder::Status::fromExceptionCode(fdp->ConsumeIntegral<int32_t>(),
+ message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ *status = binder::Status::fromServiceSpecificError(
+ fdp->ConsumeIntegral<int32_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status*, Parcel*) -> void {
+ binder::Status::exceptionToString(fdp->ConsumeIntegral<int32_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ *status = binder::Status::fromServiceSpecificError(fdp->ConsumeIntegral<
+ int32_t>(),
+ message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ *status = binder::Status::fromStatusT(fdp->ConsumeIntegral<status_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ status->setFromStatusT(fdp->ConsumeIntegral<status_t>());
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void {
+ std::stringstream ss;
+ ss << *status;
+ },
+};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
new file mode 100644
index 0000000..c950020
--- /dev/null
+++ b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Debug.h>
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstddef>
+#include <limits>
+
+// Fuzzer for the TextOutput class. These were lifted from the existing
+// test suite.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ CapturedStderr cap;
+
+ while (fdp.remaining_bytes() > 1) {
+ switch (fdp.ConsumeIntegral<uint8_t>() % 3) {
+ case 0: {
+ std::string input = fdp.ConsumeBytesAsString(
+ fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()));
+ android::aerr << input << android::endl;
+ break;
+ }
+ case 1: {
+ std::string str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ android::HexDump input(str.c_str(), sizeof(str.c_str()));
+ android::aerr << input << android::endl;
+ break;
+ }
+ case 2: {
+ android::TypeCode input(fdp.ConsumeIntegral<uint32_t>());
+ android::aerr << input << android::endl;
+ }
+ }
+ }
+ cap.Stop();
+
+ return 0;
+}
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
new file mode 100644
index 0000000..d58d9b6
--- /dev/null
+++ b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
+// Calls a function from the ops_vector
+template <class F, class T, class... Types>
+void callArbitraryFunction(F* fdp, T const& ops_vector, Types... args) {
+ // Choose which function we'll be calling
+ uint8_t function_id = fdp->template ConsumeIntegralInRange<uint8_t>(0, ops_vector.size() - 1);
+
+ // Call the function we've chosen
+ ops_vector[function_id](fdp, args...);
+}
+
+template <class T>
+T getArbitraryVectorElement(FuzzedDataProvider* fdp, std::vector<T> const& vect, bool allow_null) {
+ // If we're allowing null, give it a 50:50 shot at returning a nullptr
+ if (vect.empty() || (allow_null && fdp->ConsumeBool())) {
+ return nullptr;
+ }
+
+ // Otherwise, return an element from our vector
+ return vect.at(fdp->ConsumeIntegralInRange<size_t>(0, vect.size() - 1));
+}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 6964324..4ecbe53 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -61,4 +61,16 @@
return mNameToService.find(name) != mNameToService.end();
}
+Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+ Vector<String16> out;
+ const String16 prefix = name + String16("/");
+ for (const auto& [registeredName, service] : mNameToService) {
+ (void) service;
+ if (registeredName.startsWith(prefix)) {
+ out.add(String16(registeredName.string() + prefix.size()));
+ }
+ }
+ return out;
+}
+
} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
index 62311d4..4ef47fb 100644
--- a/libs/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -30,42 +30,23 @@
public:
ServiceManager();
- /**
- * Equivalent of checkService.
- */
sp<IBinder> getService( const String16& name) const override;
- /**
- * Retrieve an existing service, non-blocking.
- */
sp<IBinder> checkService( const String16& name) const override;
- /**
- * Register a service.
- */
status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated = false,
int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
- /**
- * Return list of all existing services.
- */
Vector<String16> listServices(int dumpsysFlags = 0) override;
IBinder* onAsBinder() override;
- /**
- * Effectively no-oped in this implementation - equivalent to checkService.
- */
sp<IBinder> waitForService(const String16& name) override;
- /**
- * Check if a service is declared (e.g. VINTF manifest).
- *
- * If this returns true, waitForService should always be able to return the
- * service.
- */
- bool isDeclared(const String16& name) override;
+ bool isDeclared(const String16& name) override;
+
+ Vector<String16> getDeclaredInstances(const String16& iface) override;
private:
std::map<String16, sp<IBinder>> mNameToService;
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 53c68b7..e2f072a 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -706,35 +706,35 @@
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBytes), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.sampleIncrementInBits), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.strideInBytes), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.widthInSamples), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.heightInSamples), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.totalSizeInBytes), output);
if (err) {
return err;
}
- err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.horizontalSubsampling), output);
if (err) {
return err;
}
- return encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+ return encodeInteger<int64_t>(static_cast<int64_t>(input.verticalSubsampling), output);
}
status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 119b3e0..55c5de9 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -466,7 +466,8 @@
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const std::string developerOptIn, const int rulesFd,
+ const std::string developerOptIn,
+ const std::vector<std::string> eglFeatures, const int rulesFd,
const long rulesOffset, const long rulesLength) {
if (mUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
@@ -475,6 +476,8 @@
return;
}
+ mAngleEglFeatures = std::move(eglFeatures);
+
ALOGV("setting ANGLE path to '%s'", path.c_str());
mAnglePath = path;
ALOGV("setting ANGLE app name to '%s'", appName.c_str());
@@ -520,6 +523,10 @@
return mAngleAppName;
}
+const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() {
+ return mAngleEglFeatures;
+}
+
const std::string& GraphicsEnv::getLayerPaths() {
return mLayerPaths;
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 22a2332..900fc49 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,12 +97,15 @@
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
- const int rulesFd, const long rulesOffset, const long rulesLength);
+ const std::vector<std::string> eglFeatures, const int rulesFd,
+ const long rulesOffset, const long rulesLength);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app name for ANGLE debug message.
std::string& getAngleAppName();
+ const std::vector<std::string>& getAngleEglFeatures();
+
/*
* Apis for debug layer
*/
@@ -154,6 +157,8 @@
std::string mAngleAppName;
// ANGLE developer opt in status.
std::string mAngleDeveloperOptIn;
+ // ANGLE EGL features;
+ std::vector<std::string> mAngleEglFeatures;
// ANGLE rules.
std::vector<char> mRulesBuffer;
// Use ANGLE flag.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 686e274..a0e9cbf 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -30,6 +30,12 @@
min_sdk_version: "29",
}
+filegroup {
+ name: "libgui_aidl",
+ srcs: ["aidl/**/*.aidl"],
+ path: "aidl/",
+}
+
cc_library_shared {
name: "libgui",
vendor_available: false,
@@ -42,6 +48,7 @@
srcs: [
":framework_native_aidl",
+ ":libgui_aidl",
":libgui_bufferqueue_sources",
"BitTube.cpp",
@@ -55,13 +62,13 @@
"DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
- "GuiConfig.cpp",
"IConsumerListener.cpp",
"IDisplayEventConnection.cpp",
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
"IRegionSamplingListener.cpp",
+ "IScreenCaptureListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
@@ -74,6 +81,7 @@
"SurfaceControl.cpp",
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
+ "TransactionTracing.cpp",
"view/Surface.cpp",
"bufferqueue/1.0/B2HProducerListener.cpp",
"bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
@@ -145,6 +153,7 @@
defaults: ["libgui_bufferqueue-defaults"],
srcs: [
+ ":libgui_aidl",
":libgui_bufferqueue_sources",
],
}
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 56591bd..87f7972 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "BLASTBufferQueue"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
@@ -29,8 +30,20 @@
using namespace std::chrono_literals;
+namespace {
+inline const char* toString(bool b) {
+ return b ? "true" : "false";
+}
+} // namespace
+
namespace android {
+// Macros to include adapter info in log messages
+#define BQA_LOGV(x, ...) \
+ ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+#define BQA_LOGE(x, ...) \
+ ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__)
+
void BLASTBufferItemConsumer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mPreviouslyConnected = mCurrentlyConnected;
@@ -93,9 +106,10 @@
if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
}
-BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
- bool enableTripleBuffering)
- : mSurfaceControl(surface),
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
+ int width, int height, bool enableTripleBuffering)
+ : mName(name),
+ mSurfaceControl(surface),
mWidth(width),
mHeight(height),
mNextTransaction(nullptr) {
@@ -110,9 +124,9 @@
mBufferItemConsumer =
new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true);
static int32_t id = 0;
- auto name = std::string("BLAST Consumer") + std::to_string(id);
+ auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
id++;
- mBufferItemConsumer->setName(String8(name.c_str()));
+ mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
@@ -127,7 +141,7 @@
mPendingReleaseItem.releaseFence = nullptr;
}
-void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
+void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height) {
std::unique_lock _lock{mMutex};
mSurfaceControl = surface;
@@ -152,6 +166,8 @@
const std::vector<SurfaceControlStats>& stats) {
std::unique_lock _lock{mMutex};
ATRACE_CALL();
+ BQA_LOGV("transactionCallback");
+ mInitialCallbackReceived = true;
if (!stats.empty()) {
mTransformHint = stats[0].transformHint;
@@ -169,7 +185,7 @@
if (!stats.empty()) {
mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
} else {
- ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+ BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
mPendingReleaseItem.releaseFence = nullptr;
}
mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
@@ -182,7 +198,7 @@
}
if (mSubmitted.empty()) {
- ALOGE("ERROR: callback with no corresponding submitted buffer item");
+ BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
}
mPendingReleaseItem.item = std::move(mSubmitted.front());
mSubmitted.pop();
@@ -195,12 +211,20 @@
void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
ATRACE_CALL();
- if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+ BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
+
+ // Wait to acquire a buffer if there are no frames available or we have acquired the maximum
+ // number of buffers.
+ // As a special case, we wait for the first callback before acquiring the second buffer so we
+ // can ensure the first buffer is presented if multiple buffers are queued in succession.
+ if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1 ||
+ (!mInitialCallbackReceived && mNumAcquired == 1)) {
+ BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
return;
}
if (mSurfaceControl == nullptr) {
- ALOGE("ERROR : surface control is null");
+ BQA_LOGE("ERROR : surface control is null");
return;
}
@@ -227,6 +251,13 @@
return;
}
+ if (rejectBuffer(bufferItem)) {
+ BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth,
+ mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ return;
+ }
+
mNumAcquired++;
mSubmitted.push(bufferItem);
@@ -246,15 +277,22 @@
bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
- t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
+ t->setFrame(mSurfaceControl,
+ {0, 0, static_cast<int32_t>(mWidth), static_cast<int32_t>(mHeight)});
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
t->setDesiredPresentTime(bufferItem.mTimestamp);
+ t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
if (applyTransaction) {
t->apply();
}
+
+ BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
+ " applyTransaction=%s mTimestamp=%" PRId64,
+ mWidth, mHeight, bufferItem.mFrameNumber, toString(applyTransaction),
+ bufferItem.mTimestamp);
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -268,7 +306,10 @@
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- if (mNextTransaction != nullptr) {
+ const bool nextTransactionSet = mNextTransaction != nullptr;
+ BQA_LOGV("onFrameAvailable nextTransactionSet=%s", toString(nextTransactionSet));
+
+ if (nextTransactionSet) {
while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
mCallbackCV.wait(_lock);
}
@@ -283,4 +324,21 @@
mNextTransaction = t;
}
+bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) const {
+ if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {
+ // Only reject buffers if scaling mode is freeze.
+ return false;
+ }
+
+ uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+ uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+ // Take the buffer's orientation into account
+ if (item.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ // reject buffers if the buffer size doesn't match.
+ return bufWidth != mWidth || bufHeight != mHeight;
+}
} // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 2cc7c34..7e894b4 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -73,7 +73,8 @@
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ int64_t vsyncId;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncId)) {
ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
}
@@ -116,11 +117,13 @@
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
- ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%s, count=%d", this,
- ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount);
+ int64_t vsyncId;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncId)) {
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+ ", displayId=%s, count=%d, vsyncId=%" PRId64,
+ this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, vsyncId);
mWaitingForVsync = false;
- dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncId);
}
return 1; // keep the callback
@@ -128,10 +131,11 @@
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
- uint32_t* outCount) {
+ uint32_t* outCount, int64_t* outVsyncId) {
bool gotVsync = false;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
+ *outVsyncId = 0;
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
for (ssize_t i = 0; i < n; i++) {
@@ -144,6 +148,7 @@
*outTimestamp = ev.header.timestamp;
*outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
+ *outVsyncId = ev.vsync.vsyncId;
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/IScreenCaptureListener.cpp b/libs/gui/IScreenCaptureListener.cpp
new file mode 100644
index 0000000..0635e9c
--- /dev/null
+++ b/libs/gui/IScreenCaptureListener.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 <gui/IScreenCaptureListener.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+ ON_SCREEN_CAPTURE_COMPLETE = IBinder::FIRST_CALL_TRANSACTION,
+ LAST = ON_SCREEN_CAPTURE_COMPLETE,
+};
+
+} // Anonymous namespace
+
+class BpScreenCaptureListener : public SafeBpInterface<IScreenCaptureListener> {
+public:
+ explicit BpScreenCaptureListener(const sp<IBinder>& impl)
+ : SafeBpInterface<IScreenCaptureListener>(impl, "BpScreenCaptureListener") {}
+
+ ~BpScreenCaptureListener() override;
+
+ status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IScreenCaptureListener::getInterfaceDescriptor());
+
+ SAFE_PARCEL(captureResults.write, data);
+ return remote()->transact(static_cast<uint32_t>(Tag::ON_SCREEN_CAPTURE_COMPLETE), data,
+ &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpScreenCaptureListener::~BpScreenCaptureListener() = default;
+
+IMPLEMENT_META_INTERFACE(ScreenCaptureListener, "android.gui.IScreenCaptureListener");
+
+status_t BnScreenCaptureListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags) {
+ auto tag = static_cast<Tag>(code);
+ switch (tag) {
+ case Tag::ON_SCREEN_CAPTURE_COMPLETE: {
+ CHECK_INTERFACE(IScreenCaptureListener, data, reply);
+ ScreenCaptureResults captureResults;
+ SAFE_PARCEL(captureResults.read, data);
+ return onScreenCaptureComplete(captureResults);
+ }
+ default: {
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 4a12035..964195d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -20,6 +20,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/ITransactionTraceListener.h>
+
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -66,42 +68,43 @@
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
- virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& commands,
- int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) {
+ virtual status_t setTransactionState(
+ int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& commands, int64_t desiredPresentTime,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeUint32(static_cast<uint32_t>(state.size()));
+ SAFE_PARCEL(data.writeInt64, frameTimelineVsyncId);
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
for (const auto& s : state) {
- s.write(data);
+ SAFE_PARCEL(s.write, data);
}
- data.writeUint32(static_cast<uint32_t>(displays.size()));
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
for (const auto& d : displays) {
- d.write(data);
+ SAFE_PARCEL(d.write, data);
}
- data.writeUint32(flags);
- data.writeStrongBinder(applyToken);
- commands.write(data);
- data.writeInt64(desiredPresentTime);
- data.writeStrongBinder(uncacheBuffer.token.promote());
- data.writeUint64(uncacheBuffer.id);
- data.writeBool(hasListenerCallbacks);
+ SAFE_PARCEL(data.writeUint32, flags);
+ SAFE_PARCEL(data.writeStrongBinder, applyToken);
+ SAFE_PARCEL(commands.write, data);
+ SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+ SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+ SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
- if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
- data.writeStrongBinder(listener);
- data.writeInt64Vector(callbackIds);
- }
+ SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ SAFE_PARCEL(data.writeStrongBinder, listener);
+ SAFE_PARCEL(data.writeInt64Vector, callbackIds);
}
- remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
+ SAFE_PARCEL(data.writeUint64, transactionId);
+
+ return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
}
virtual void bootFinished()
@@ -112,75 +115,33 @@
}
virtual status_t captureDisplay(const DisplayCaptureArgs& args,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(args.write, data);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- status_t result = args.write(data);
- if (result != NO_ERROR) {
- ALOGE("captureDisplay failed to parcel args: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureDisplay failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result != NO_ERROR) {
- ALOGE("captureDisplay failed to readInt32: %d", result);
- return result;
- }
-
- captureResults.read(reply);
- return result;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
}
virtual status_t captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeUint64(displayOrLayerStack);
- status_t result =
- remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureDisplay failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result != NO_ERROR) {
- ALOGE("captureDisplay failed to readInt32: %d", result);
- return result;
- }
+ SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- captureResults.read(reply);
- return result;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
}
virtual status_t captureLayers(const LayerCaptureArgs& args,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(args.write, data);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- status_t result = args.write(data);
- if (result != NO_ERROR) {
- ALOGE("captureLayers failed to parcel args: %d", result);
- return result;
- }
-
- result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureLayers failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32();
- if (result != NO_ERROR) {
- ALOGE("captureLayers failed to readInt32: %d", result);
- return result;
- }
-
- captureResults.read(reply);
- return result;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
}
virtual bool authenticateSurfaceTexture(
@@ -289,10 +250,25 @@
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeString8(displayName);
- data.writeInt32(secure ? 1 : 0);
- remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);
- return reply.readStrongBinder();
+ status_t status = data.writeString8(displayName);
+ if (status) {
+ return nullptr;
+ }
+ status = data.writeBool(secure);
+ if (status) {
+ return nullptr;
+ }
+
+ status = remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);
+ if (status) {
+ return nullptr;
+ }
+ sp<IBinder> display;
+ status = reply.readNullableStrongBinder(&display);
+ if (status) {
+ return nullptr;
+ }
+ return display;
}
virtual void destroyDisplay(const sp<IBinder>& display)
@@ -1195,6 +1171,47 @@
return NO_ERROR;
}
+
+ virtual status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+ int64_t frameTimelineVsyncId) {
+ Parcel data, reply;
+ status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineVsync: failed writing interface token: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ err = data.writeStrongBinder(IInterface::asBinder(surface));
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineVsync: failed writing strong binder: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ err = data.writeInt64(frameTimelineVsyncId);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineVsync: failed writing int64_t: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ err = remote()->transact(BnSurfaceComposer::SET_FRAME_TIMELINE_VSYNC, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineVsync: failed to transact: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ return reply.readInt32();
+ }
+
+ virtual status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+ return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply);
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1218,59 +1235,63 @@
case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- size_t count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
+ int64_t frameTimelineVsyncId;
+ SAFE_PARCEL(data.readInt64, &frameTimelineVsyncId);
+ uint32_t count = 0;
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
Vector<ComposerState> state;
state.setCapacity(count);
for (size_t i = 0; i < count; i++) {
ComposerState s;
- if (s.read(data) == BAD_VALUE) {
- return BAD_VALUE;
- }
+ SAFE_PARCEL(s.read, data);
state.add(s);
}
- count = data.readUint32();
- if (count > data.dataSize()) {
- return BAD_VALUE;
- }
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
DisplayState d;
Vector<DisplayState> displays;
displays.setCapacity(count);
for (size_t i = 0; i < count; i++) {
- if (d.read(data) == BAD_VALUE) {
- return BAD_VALUE;
- }
+ SAFE_PARCEL(d.read, data);
displays.add(d);
}
- uint32_t stateFlags = data.readUint32();
- sp<IBinder> applyToken = data.readStrongBinder();
+ uint32_t stateFlags = 0;
+ SAFE_PARCEL(data.readUint32, &stateFlags);
+ sp<IBinder> applyToken;
+ SAFE_PARCEL(data.readStrongBinder, &applyToken);
InputWindowCommands inputWindowCommands;
- inputWindowCommands.read(data);
+ SAFE_PARCEL(inputWindowCommands.read, data);
- int64_t desiredPresentTime = data.readInt64();
+ int64_t desiredPresentTime = 0;
+ SAFE_PARCEL(data.readInt64, &desiredPresentTime);
client_cache_t uncachedBuffer;
- uncachedBuffer.token = data.readStrongBinder();
- uncachedBuffer.id = data.readUint64();
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+ uncachedBuffer.token = tmpBinder;
+ SAFE_PARCEL(data.readUint64, &uncachedBuffer.id);
- bool hasListenerCallbacks = data.readBool();
+ bool hasListenerCallbacks = false;
+ SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
std::vector<ListenerCallbacks> listenerCallbacks;
- int32_t listenersSize = data.readInt32();
+ int32_t listenersSize = 0;
+ SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
for (int32_t i = 0; i < listenersSize; i++) {
- auto listener = data.readStrongBinder();
+ SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
std::vector<CallbackId> callbackIds;
- data.readInt64Vector(&callbackIds);
- listenerCallbacks.emplace_back(listener, callbackIds);
+ SAFE_PARCEL(data.readInt64Vector, &callbackIds);
+ listenerCallbacks.emplace_back(tmpBinder, callbackIds);
}
- setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
- desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
- listenerCallbacks);
- return NO_ERROR;
+
+ uint64_t transactionId = -1;
+ SAFE_PARCEL(data.readUint64, &transactionId);
+
+ return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags,
+ applyToken, inputWindowCommands, desiredPresentTime,
+ uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+ transactionId);
}
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1280,50 +1301,29 @@
case CAPTURE_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayCaptureArgs args;
- ScreenCaptureResults captureResults;
+ sp<IScreenCaptureListener> captureListener;
+ SAFE_PARCEL(args.read, data);
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
- status_t res = args.read(data);
- if (res != NO_ERROR) {
- reply->writeInt32(res);
- return NO_ERROR;
- }
-
- res = captureDisplay(args, captureResults);
-
- reply->writeInt32(res);
- if (res == NO_ERROR) {
- captureResults.write(*reply);
- }
- return NO_ERROR;
+ return captureDisplay(args, captureListener);
}
case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- uint64_t displayOrLayerStack = data.readUint64();
- ScreenCaptureResults captureResults;
- status_t res = captureDisplay(displayOrLayerStack, captureResults);
- reply->writeInt32(res);
- if (res == NO_ERROR) {
- captureResults.write(*reply);
- }
- return NO_ERROR;
+ uint64_t displayOrLayerStack = 0;
+ sp<IScreenCaptureListener> captureListener;
+ SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
+
+ return captureDisplay(displayOrLayerStack, captureListener);
}
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
LayerCaptureArgs args;
- ScreenCaptureResults captureResults;
+ sp<IScreenCaptureListener> captureListener;
+ SAFE_PARCEL(args.read, data);
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
- status_t res = args.read(data);
- if (res != NO_ERROR) {
- reply->writeInt32(res);
- return NO_ERROR;
- }
-
- res = captureLayers(args, captureResults);
- reply->writeInt32(res);
- if (res == NO_ERROR) {
- captureResults.write(*reply);
- }
- return NO_ERROR;
+ return captureLayers(args, captureListener);
}
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1364,10 +1364,12 @@
}
case CREATE_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- String8 displayName = data.readString8();
- bool secure = bool(data.readInt32());
- sp<IBinder> display(createDisplay(displayName, secure));
- reply->writeStrongBinder(display);
+ String8 displayName;
+ SAFE_PARCEL(data.readString8, &displayName);
+ bool secure = false;
+ SAFE_PARCEL(data.readBool, &secure);
+ sp<IBinder> display = createDisplay(displayName, secure);
+ SAFE_PARCEL(reply->writeStrongBinder, display);
return NO_ERROR;
}
case DESTROY_DISPLAY: {
@@ -2019,6 +2021,40 @@
}
return NO_ERROR;
}
+ case SET_FRAME_TIMELINE_VSYNC: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> binder;
+ status_t err = data.readStrongBinder(&binder);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineVsync: failed to read strong binder: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+ sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
+ if (!surface) {
+ ALOGE("setFrameTimelineVsync: failed to cast to IGraphicBufferProducer: %s (%d)",
+ strerror(-err), -err);
+ return err;
+ }
+ int64_t frameTimelineVsyncId;
+ err = data.readInt64(&frameTimelineVsyncId);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameTimelineVsync: failed to read int64_t: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
+
+ status_t result = setFrameTimelineVsync(surface, frameTimelineVsyncId);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+ case ADD_TRANSACTION_TRACE_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<gui::ITransactionTraceListener> listener;
+ SAFE_PARCEL(data.readStrongBinder, &listener);
+
+ return addTransactionTraceListener(listener);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 621cf59..5e7a7ec 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -50,12 +50,12 @@
status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
- uint32_t* outTransformHint) override {
+ int32_t* outLayerId, uint32_t* outTransformHint) override {
return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
name, width, height,
format, flags, parent,
std::move(metadata),
- handle, gbp,
+ handle, gbp, outLayerId,
outTransformHint);
}
@@ -63,14 +63,14 @@
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint) override {
return callRemote<decltype(
&ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
name, width, height, format,
flags, parent,
std::move(metadata), handle, gbp,
- outTransformHint);
+ outLayerId, outTransformHint);
}
status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -85,10 +85,11 @@
outStats);
}
- status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override {
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) override {
return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
mirrorFromHandle,
- outHandle);
+ outHandle, outLayerId);
}
};
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 725d3cd..8594ab3 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -18,193 +18,241 @@
#include <inttypes.h>
-#include <utils/Errors.h>
#include <binder/Parcel.h>
-#include <gui/ISurfaceComposerClient.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
+#include <utils/Errors.h>
#include <cmath>
namespace android {
+layer_state_t::layer_state_t()
+ : what(0),
+ x(0),
+ y(0),
+ z(0),
+ w(0),
+ h(0),
+ layerStack(0),
+ alpha(0),
+ flags(0),
+ mask(0),
+ reserved(0),
+ crop_legacy(Rect::INVALID_RECT),
+ cornerRadius(0.0f),
+ backgroundBlurRadius(0),
+ barrierFrameNumber(0),
+ overrideScalingMode(-1),
+ transform(0),
+ transformToDisplayInverse(false),
+ crop(Rect::INVALID_RECT),
+ orientedDisplaySpaceRect(Rect::INVALID_RECT),
+ dataspace(ui::Dataspace::UNKNOWN),
+ surfaceDamageRegion(),
+ api(-1),
+ colorTransform(mat4()),
+ bgColorAlpha(0),
+ bgColorDataspace(ui::Dataspace::UNKNOWN),
+ colorSpaceAgnostic(false),
+ shadowRadius(0.0f),
+ frameRateSelectionPriority(-1),
+ frameRate(0.0f),
+ frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ fixedTransformHint(ui::Transform::ROT_INVALID),
+ frameNumber(0) {
+ matrix.dsdx = matrix.dtdy = 1.0f;
+ matrix.dsdy = matrix.dtdx = 0.0f;
+ hdrMetadata.validTypes = 0;
+}
+
status_t layer_state_t::write(Parcel& output) const
{
- output.writeStrongBinder(surface);
- output.writeUint64(what);
- output.writeFloat(x);
- output.writeFloat(y);
- output.writeInt32(z);
- output.writeUint32(w);
- output.writeUint32(h);
- output.writeUint32(layerStack);
- output.writeFloat(alpha);
- output.writeUint32(flags);
- output.writeUint32(mask);
- *reinterpret_cast<layer_state_t::matrix22_t *>(
- output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
- output.write(crop_legacy);
- output.writeStrongBinder(barrierHandle_legacy);
- output.writeStrongBinder(reparentHandle);
- output.writeUint64(frameNumber_legacy);
- output.writeInt32(overrideScalingMode);
- output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy));
- output.writeStrongBinder(relativeLayerHandle);
- output.writeStrongBinder(parentHandleForChild);
- output.writeFloat(color.r);
- output.writeFloat(color.g);
- output.writeFloat(color.b);
+ SAFE_PARCEL(output.writeStrongBinder, surface);
+ SAFE_PARCEL(output.writeInt32, layerId);
+ SAFE_PARCEL(output.writeUint64, what);
+ SAFE_PARCEL(output.writeFloat, x);
+ SAFE_PARCEL(output.writeFloat, y);
+ SAFE_PARCEL(output.writeInt32, z);
+ SAFE_PARCEL(output.writeUint32, w);
+ SAFE_PARCEL(output.writeUint32, h);
+ SAFE_PARCEL(output.writeUint32, layerStack);
+ 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_legacy);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
+ SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
+ SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
+ SAFE_PARCEL(output.writeInt32, overrideScalingMode);
+ 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);
#ifndef NO_INPUT
- inputHandle->writeToParcel(&output);
+ SAFE_PARCEL(inputHandle->writeToParcel, &output);
#endif
- output.write(transparentRegion);
- output.writeUint32(transform);
- output.writeBool(transformToDisplayInverse);
- output.write(crop);
- output.write(orientedDisplaySpaceRect);
+ SAFE_PARCEL(output.write, transparentRegion);
+ SAFE_PARCEL(output.writeUint32, transform);
+ SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
+ SAFE_PARCEL(output.write, crop);
+ SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
+
if (buffer) {
- output.writeBool(true);
- output.write(*buffer);
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *buffer);
} else {
- output.writeBool(false);
+ SAFE_PARCEL(output.writeBool, false);
}
+
if (acquireFence) {
- output.writeBool(true);
- output.write(*acquireFence);
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *acquireFence);
} else {
- output.writeBool(false);
+ SAFE_PARCEL(output.writeBool, false);
}
- output.writeUint32(static_cast<uint32_t>(dataspace));
- output.write(hdrMetadata);
- output.write(surfaceDamageRegion);
- output.writeInt32(api);
+
+ SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
+ SAFE_PARCEL(output.write, hdrMetadata);
+ SAFE_PARCEL(output.write, surfaceDamageRegion);
+ SAFE_PARCEL(output.writeInt32, api);
+
if (sidebandStream) {
- output.writeBool(true);
- output.writeNativeHandle(sidebandStream->handle());
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.writeNativeHandle, sidebandStream->handle());
} else {
- output.writeBool(false);
+ SAFE_PARCEL(output.writeBool, false);
}
- memcpy(output.writeInplace(16 * sizeof(float)),
- colorTransform.asArray(), 16 * sizeof(float));
- output.writeFloat(cornerRadius);
- output.writeUint32(backgroundBlurRadius);
- output.writeStrongBinder(cachedBuffer.token.promote());
- output.writeUint64(cachedBuffer.id);
- output.writeParcelable(metadata);
-
- output.writeFloat(bgColorAlpha);
- output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
- output.writeBool(colorSpaceAgnostic);
-
- auto err = output.writeVectorSize(listeners);
- if (err) {
- return err;
- }
+ SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
+ SAFE_PARCEL(output.writeFloat, cornerRadius);
+ SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
+ SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
+ SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
+ SAFE_PARCEL(output.writeParcelable, metadata);
+ SAFE_PARCEL(output.writeFloat, bgColorAlpha);
+ SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
+ SAFE_PARCEL(output.writeBool, colorSpaceAgnostic);
+ SAFE_PARCEL(output.writeVectorSize, listeners);
for (auto listener : listeners) {
- err = output.writeStrongBinder(listener.transactionCompletedListener);
- if (err) {
- return err;
- }
- err = output.writeInt64Vector(listener.callbackIds);
- if (err) {
- return err;
- }
+ SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener);
+ SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds);
}
- output.writeFloat(shadowRadius);
- output.writeInt32(frameRateSelectionPriority);
- output.writeFloat(frameRate);
- output.writeByte(frameRateCompatibility);
- output.writeUint32(fixedTransformHint);
+ SAFE_PARCEL(output.writeFloat, shadowRadius);
+ SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
+ SAFE_PARCEL(output.writeFloat, frameRate);
+ SAFE_PARCEL(output.writeByte, frameRateCompatibility);
+ SAFE_PARCEL(output.writeUint32, fixedTransformHint);
+ SAFE_PARCEL(output.writeUint64, frameNumber);
return NO_ERROR;
}
status_t layer_state_t::read(const Parcel& input)
{
- surface = input.readStrongBinder();
- what = input.readUint64();
- x = input.readFloat();
- y = input.readFloat();
- z = input.readInt32();
- w = input.readUint32();
- h = input.readUint32();
- layerStack = input.readUint32();
- alpha = input.readFloat();
- flags = static_cast<uint8_t>(input.readUint32());
- mask = static_cast<uint8_t>(input.readUint32());
- const void* matrix_data = input.readInplace(sizeof(layer_state_t::matrix22_t));
- if (matrix_data) {
- matrix = *reinterpret_cast<layer_state_t::matrix22_t const *>(matrix_data);
- } else {
- return BAD_VALUE;
- }
- input.read(crop_legacy);
- barrierHandle_legacy = input.readStrongBinder();
- reparentHandle = input.readStrongBinder();
- frameNumber_legacy = input.readUint64();
- overrideScalingMode = input.readInt32();
- barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
- relativeLayerHandle = input.readStrongBinder();
- parentHandleForChild = input.readStrongBinder();
- color.r = input.readFloat();
- color.g = input.readFloat();
- color.b = input.readFloat();
+ SAFE_PARCEL(input.readNullableStrongBinder, &surface);
+ SAFE_PARCEL(input.readInt32, &layerId);
+ SAFE_PARCEL(input.readUint64, &what);
+ SAFE_PARCEL(input.readFloat, &x);
+ SAFE_PARCEL(input.readFloat, &y);
+ SAFE_PARCEL(input.readInt32, &z);
+ SAFE_PARCEL(input.readUint32, &w);
+ SAFE_PARCEL(input.readUint32, &h);
+ SAFE_PARCEL(input.readUint32, &layerStack);
+ SAFE_PARCEL(input.readFloat, &alpha);
+ uint32_t tmpUint32 = 0;
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ flags = static_cast<uint8_t>(tmpUint32);
+
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ mask = static_cast<uint8_t>(tmpUint32);
+
+ SAFE_PARCEL(matrix.read, input);
+ SAFE_PARCEL(input.read, crop_legacy);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
+ SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
+ SAFE_PARCEL(input.readInt32, &overrideScalingMode);
+
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
+ SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
+
+ float tmpFloat = 0;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.r = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.g = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ color.b = tmpFloat;
#ifndef NO_INPUT
- inputHandle->readFromParcel(&input);
+ SAFE_PARCEL(inputHandle->readFromParcel, &input);
#endif
- input.read(transparentRegion);
- transform = input.readUint32();
- transformToDisplayInverse = input.readBool();
- input.read(crop);
- input.read(orientedDisplaySpaceRect);
- buffer = new GraphicBuffer();
- if (input.readBool()) {
- input.read(*buffer);
+ SAFE_PARCEL(input.read, transparentRegion);
+ SAFE_PARCEL(input.readUint32, &transform);
+ SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
+ SAFE_PARCEL(input.read, crop);
+ SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
+
+ bool tmpBool = false;
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(input.read, *buffer);
}
- acquireFence = new Fence();
- if (input.readBool()) {
- input.read(*acquireFence);
+
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
+ acquireFence = new Fence();
+ SAFE_PARCEL(input.read, *acquireFence);
}
- dataspace = static_cast<ui::Dataspace>(input.readUint32());
- input.read(hdrMetadata);
- input.read(surfaceDamageRegion);
- api = input.readInt32();
- if (input.readBool()) {
+
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ dataspace = static_cast<ui::Dataspace>(tmpUint32);
+
+ SAFE_PARCEL(input.read, hdrMetadata);
+ SAFE_PARCEL(input.read, surfaceDamageRegion);
+ SAFE_PARCEL(input.readInt32, &api);
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
}
- const void* color_transform_data = input.readInplace(16 * sizeof(float));
- if (color_transform_data) {
- colorTransform = mat4(static_cast<const float*>(color_transform_data));
- } else {
- return BAD_VALUE;
- }
- cornerRadius = input.readFloat();
- backgroundBlurRadius = input.readUint32();
- cachedBuffer.token = input.readStrongBinder();
- cachedBuffer.id = input.readUint64();
- input.readParcelable(&metadata);
+ SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
+ SAFE_PARCEL(input.readFloat, &cornerRadius);
+ SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ cachedBuffer.token = tmpBinder;
+ SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
+ SAFE_PARCEL(input.readParcelable, &metadata);
- bgColorAlpha = input.readFloat();
- bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
- colorSpaceAgnostic = input.readBool();
+ SAFE_PARCEL(input.readFloat, &bgColorAlpha);
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32);
+ SAFE_PARCEL(input.readBool, &colorSpaceAgnostic);
- int32_t numListeners = input.readInt32();
+ int32_t numListeners = 0;
+ SAFE_PARCEL_READ_SIZE(input.readInt32, &numListeners, input.dataSize());
listeners.clear();
for (int i = 0; i < numListeners; i++) {
- auto listener = input.readStrongBinder();
+ sp<IBinder> listener;
std::vector<CallbackId> callbackIds;
- input.readInt64Vector(&callbackIds);
+ SAFE_PARCEL(input.readNullableStrongBinder, &listener);
+ SAFE_PARCEL(input.readInt64Vector, &callbackIds);
listeners.emplace_back(listener, callbackIds);
}
- shadowRadius = input.readFloat();
- frameRateSelectionPriority = input.readInt32();
- frameRate = input.readFloat();
- frameRateCompatibility = input.readByte();
- fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32());
+ SAFE_PARCEL(input.readFloat, &shadowRadius);
+ SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
+ SAFE_PARCEL(input.readFloat, &frameRate);
+ SAFE_PARCEL(input.readByte, &frameRateCompatibility);
+ SAFE_PARCEL(input.readUint32, &tmpUint32);
+ fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
+ SAFE_PARCEL(input.readUint64, &frameNumber);
return NO_ERROR;
}
@@ -225,28 +273,34 @@
height(0) {}
status_t DisplayState::write(Parcel& output) const {
- output.writeStrongBinder(token);
- output.writeStrongBinder(IInterface::asBinder(surface));
- output.writeUint32(what);
- output.writeUint32(layerStack);
- output.writeUint32(toRotationInt(orientation));
- output.write(layerStackSpaceRect);
- output.write(orientedDisplaySpaceRect);
- output.writeUint32(width);
- output.writeUint32(height);
+ SAFE_PARCEL(output.writeStrongBinder, token);
+ SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
+ SAFE_PARCEL(output.writeUint32, what);
+ SAFE_PARCEL(output.writeUint32, layerStack);
+ SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
+ SAFE_PARCEL(output.write, layerStackSpaceRect);
+ SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
+ SAFE_PARCEL(output.writeUint32, width);
+ SAFE_PARCEL(output.writeUint32, height);
return NO_ERROR;
}
status_t DisplayState::read(const Parcel& input) {
- token = input.readStrongBinder();
- surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
- what = input.readUint32();
- layerStack = input.readUint32();
- orientation = ui::toRotation(input.readUint32());
- input.read(layerStackSpaceRect);
- input.read(orientedDisplaySpaceRect);
- width = input.readUint32();
- height = input.readUint32();
+ SAFE_PARCEL(input.readStrongBinder, &token);
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
+
+ SAFE_PARCEL(input.readUint32, &what);
+ SAFE_PARCEL(input.readUint32, &layerStack);
+ uint32_t tmpUint = 0;
+ SAFE_PARCEL(input.readUint32, &tmpUint);
+ orientation = ui::toRotation(tmpUint);
+
+ SAFE_PARCEL(input.read, layerStackSpaceRect);
+ SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
+ SAFE_PARCEL(input.readUint32, &width);
+ SAFE_PARCEL(input.readUint32, &height);
return NO_ERROR;
}
@@ -324,9 +378,8 @@
}
if (other.what & eDeferTransaction_legacy) {
what |= eDeferTransaction_legacy;
- barrierHandle_legacy = other.barrierHandle_legacy;
- barrierGbp_legacy = other.barrierGbp_legacy;
- frameNumber_legacy = other.frameNumber_legacy;
+ barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
+ barrierFrameNumber = other.barrierFrameNumber;
}
if (other.what & eOverrideScalingModeChanged) {
what |= eOverrideScalingModeChanged;
@@ -334,7 +387,7 @@
}
if (other.what & eReparentChildren) {
what |= eReparentChildren;
- reparentHandle = other.reparentHandle;
+ reparentSurfaceControl = other.reparentSurfaceControl;
}
if (other.what & eDetachChildren) {
what |= eDetachChildren;
@@ -343,11 +396,11 @@
what |= eRelativeLayerChanged;
what &= ~eLayerChanged;
z = other.z;
- relativeLayerHandle = other.relativeLayerHandle;
+ relativeLayerSurfaceControl = other.relativeLayerSurfaceControl;
}
if (other.what & eReparent) {
what |= eReparent;
- parentHandleForChild = other.parentHandleForChild;
+ parentSurfaceControlForChild = other.parentSurfaceControlForChild;
}
if (other.what & eDestroySurface) {
what |= eDestroySurface;
@@ -442,6 +495,10 @@
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
}
+ if (other.what & eFrameNumberChanged) {
+ what |= eFrameNumberChanged;
+ frameNumber = other.frameNumber;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIu64 " what=0x%" PRIu64,
@@ -449,6 +506,22 @@
}
}
+status_t layer_state_t::matrix22_t::write(Parcel& output) const {
+ SAFE_PARCEL(output.writeFloat, dsdx);
+ SAFE_PARCEL(output.writeFloat, dtdx);
+ SAFE_PARCEL(output.writeFloat, dtdy);
+ SAFE_PARCEL(output.writeFloat, dsdy);
+ return NO_ERROR;
+}
+
+status_t layer_state_t::matrix22_t::read(const Parcel& input) {
+ SAFE_PARCEL(input.readFloat, &dsdx);
+ SAFE_PARCEL(input.readFloat, &dtdx);
+ SAFE_PARCEL(input.readFloat, &dtdy);
+ SAFE_PARCEL(input.readFloat, &dsdy);
+ return NO_ERROR;
+}
+
// ------------------------------- InputWindowCommands ----------------------------------------
bool InputWindowCommands::merge(const InputWindowCommands& other) {
@@ -463,6 +536,14 @@
return changes;
}
+bool InputWindowCommands::empty() const {
+ bool empty = true;
+#ifndef NO_INPUT
+ empty = focusRequests.empty() && !syncInputWindows;
+#endif
+ return empty;
+}
+
void InputWindowCommands::clear() {
#ifndef NO_INPUT
focusRequests.clear();
@@ -470,18 +551,20 @@
syncInputWindows = false;
}
-void InputWindowCommands::write(Parcel& output) const {
+status_t InputWindowCommands::write(Parcel& output) const {
#ifndef NO_INPUT
- output.writeParcelableVector(focusRequests);
+ SAFE_PARCEL(output.writeParcelableVector, focusRequests);
#endif
- output.writeBool(syncInputWindows);
+ SAFE_PARCEL(output.writeBool, syncInputWindows);
+ return NO_ERROR;
}
-void InputWindowCommands::read(const Parcel& input) {
+status_t InputWindowCommands::read(const Parcel& input) {
#ifndef NO_INPUT
- input.readParcelableVector(&focusRequests);
+ SAFE_PARCEL(input.readParcelableVector, &focusRequests);
#endif
- syncInputWindows = input.readBool();
+ SAFE_PARCEL(input.readBool, &syncInputWindows);
+ return NO_ERROR;
}
bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) {
@@ -504,93 +587,105 @@
// ----------------------------------------------------------------------------
status_t CaptureArgs::write(Parcel& output) const {
- status_t status = output.writeInt32(static_cast<int32_t>(pixelFormat)) ?:
- output.write(sourceCrop) ?:
- output.writeFloat(frameScale) ?:
- output.writeBool(captureSecureLayers) ?:
- output.writeInt32(uid);
- return status;
+ SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat));
+ SAFE_PARCEL(output.write, sourceCrop);
+ SAFE_PARCEL(output.writeFloat, frameScale);
+ SAFE_PARCEL(output.writeBool, captureSecureLayers);
+ SAFE_PARCEL(output.writeInt32, uid);
+ SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace));
+ return NO_ERROR;
}
status_t CaptureArgs::read(const Parcel& input) {
- int32_t format = 0;
- status_t status = input.readInt32(&format) ?:
- input.read(sourceCrop) ?:
- input.readFloat(&frameScale) ?:
- input.readBool(&captureSecureLayers) ?:
- input.readInt32(&uid);
-
- pixelFormat = static_cast<ui::PixelFormat>(format);
- return status;
+ int32_t value = 0;
+ SAFE_PARCEL(input.readInt32, &value);
+ pixelFormat = static_cast<ui::PixelFormat>(value);
+ SAFE_PARCEL(input.read, sourceCrop);
+ SAFE_PARCEL(input.readFloat, &frameScale);
+ SAFE_PARCEL(input.readBool, &captureSecureLayers);
+ SAFE_PARCEL(input.readInt32, &uid);
+ SAFE_PARCEL(input.readInt32, &value);
+ dataspace = static_cast<ui::Dataspace>(value);
+ return NO_ERROR;
}
status_t DisplayCaptureArgs::write(Parcel& output) const {
- status_t status = CaptureArgs::write(output);
+ SAFE_PARCEL(CaptureArgs::write, output);
- status |= output.writeStrongBinder(displayToken) ?:
- output.writeUint32(width) ?:
- output.writeUint32(height) ?:
- output.writeBool(useIdentityTransform);
- return status;
+ SAFE_PARCEL(output.writeStrongBinder, displayToken);
+ SAFE_PARCEL(output.writeUint32, width);
+ SAFE_PARCEL(output.writeUint32, height);
+ SAFE_PARCEL(output.writeBool, useIdentityTransform);
+ return NO_ERROR;
}
status_t DisplayCaptureArgs::read(const Parcel& input) {
- status_t status = CaptureArgs::read(input);
+ SAFE_PARCEL(CaptureArgs::read, input);
- status |= input.readStrongBinder(&displayToken) ?:
- input.readUint32(&width) ?:
- input.readUint32(&height) ?:
- input.readBool(&useIdentityTransform);
- return status;
+ SAFE_PARCEL(input.readStrongBinder, &displayToken);
+ SAFE_PARCEL(input.readUint32, &width);
+ SAFE_PARCEL(input.readUint32, &height);
+ SAFE_PARCEL(input.readBool, &useIdentityTransform);
+ return NO_ERROR;
}
status_t LayerCaptureArgs::write(Parcel& output) const {
- status_t status = CaptureArgs::write(output);
+ SAFE_PARCEL(CaptureArgs::write, output);
- status |= output.writeStrongBinder(layerHandle);
- status |= output.writeInt32(excludeHandles.size());
+ SAFE_PARCEL(output.writeStrongBinder, layerHandle);
+ SAFE_PARCEL(output.writeInt32, excludeHandles.size());
for (auto el : excludeHandles) {
- status |= output.writeStrongBinder(el);
+ SAFE_PARCEL(output.writeStrongBinder, el);
}
- status |= output.writeBool(childrenOnly);
- return status;
+ SAFE_PARCEL(output.writeBool, childrenOnly);
+ return NO_ERROR;
}
status_t LayerCaptureArgs::read(const Parcel& input) {
- status_t status = CaptureArgs::read(input);
+ SAFE_PARCEL(CaptureArgs::read, input);
- status |= input.readStrongBinder(&layerHandle);
+ SAFE_PARCEL(input.readStrongBinder, &layerHandle);
int32_t numExcludeHandles = 0;
- status |= input.readInt32(&numExcludeHandles);
+ SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize());
excludeHandles.reserve(numExcludeHandles);
for (int i = 0; i < numExcludeHandles; i++) {
sp<IBinder> binder;
- status |= input.readStrongBinder(&binder);
+ SAFE_PARCEL(input.readStrongBinder, &binder);
excludeHandles.emplace(binder);
}
- status |= input.readBool(&childrenOnly);
- return status;
+ SAFE_PARCEL(input.readBool, &childrenOnly);
+ return NO_ERROR;
}
status_t ScreenCaptureResults::write(Parcel& output) const {
- status_t status = output.write(*buffer) ?:
- output.writeBool(capturedSecureLayers) ?:
- output.writeUint32(static_cast<uint32_t>(capturedDataspace));
- return status;
+ if (buffer != nullptr) {
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *buffer);
+ } else {
+ SAFE_PARCEL(output.writeBool, false);
+ }
+ SAFE_PARCEL(output.writeBool, capturedSecureLayers);
+ SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(capturedDataspace));
+ SAFE_PARCEL(output.writeInt32, result);
+ return NO_ERROR;
}
status_t ScreenCaptureResults::read(const Parcel& input) {
- buffer = new GraphicBuffer();
+ bool hasGraphicBuffer;
+ SAFE_PARCEL(input.readBool, &hasGraphicBuffer);
+ if (hasGraphicBuffer) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(input.read, *buffer);
+ }
+
+ SAFE_PARCEL(input.readBool, &capturedSecureLayers);
uint32_t dataspace = 0;
- status_t status = input.read(*buffer) ?:
- input.readBool(&capturedSecureLayers) ?:
- input.readUint32(&dataspace);
-
+ SAFE_PARCEL(input.readUint32, &dataspace);
capturedDataspace = static_cast<ui::Dataspace>(dataspace);
-
- return status;
+ SAFE_PARCEL(input.readInt32, &result);
+ return NO_ERROR;
}
}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index e45b3d1..9ce8442 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1207,6 +1207,9 @@
case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
res = dispatchGetLastQueuedBuffer(args);
break;
+ case NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC:
+ res = dispatchSetFrameTimelineVsync(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -1513,6 +1516,14 @@
return result;
}
+int Surface::dispatchSetFrameTimelineVsync(va_list args) {
+ ATRACE_CALL();
+ auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
+
+ ALOGV("Surface::dispatchSetFrameTimelineVsync");
+ return composerService()->setFrameTimelineVsync(mGraphicBufferProducer, frameTimelineVsyncId);
+}
+
bool Surface::transformToDisplayInverse() {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 62a3c45..0068ccf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -348,15 +348,25 @@
// ---------------------------------------------------------------------------
+// Initialize transaction id counter used to generate transaction ids
+// Transactions will start counting at 1, 0 is used for invalid transactions
+std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
+
+SurfaceComposerClient::Transaction::Transaction() {
+ mId = generateId();
+}
+
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
- : mForceSynchronous(other.mForceSynchronous),
+ : mId(other.mId),
+ mForceSynchronous(other.mForceSynchronous),
mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeup(other.mEarlyWakeup),
mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
mContainsBuffer(other.mContainsBuffer),
- mDesiredPresentTime(other.mDesiredPresentTime) {
+ mDesiredPresentTime(other.mDesiredPresentTime),
+ mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
@@ -372,6 +382,10 @@
return nullptr;
}
+int64_t SurfaceComposerClient::Transaction::generateId() {
+ return (((int64_t)getpid()) << 32) | idCounter++;
+}
+
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint32_t forceSynchronous = parcel->readUint32();
const uint32_t transactionNestCount = parcel->readUint32();
@@ -381,6 +395,7 @@
const bool explicitEarlyWakeupEnd = parcel->readBool();
const bool containsBuffer = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
+ const int64_t frameTimelineVsyncId = parcel->readInt64();
size_t count = static_cast<size_t>(parcel->readUint32());
if (count > parcel->dataSize()) {
@@ -418,7 +433,7 @@
}
for (size_t j = 0; j < numSurfaces; j++) {
sp<SurfaceControl> surface;
- surface = SurfaceControl::readFromParcel(parcel);
+ SAFE_PARCEL(SurfaceControl::readFromParcel, *parcel, &surface);
listenerCallbacks[listener].surfaceControls.insert(surface);
}
}
@@ -430,12 +445,14 @@
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
composerStates.reserve(count);
for (size_t i = 0; i < count; i++) {
- sp<IBinder> surfaceControlHandle = parcel->readStrongBinder();
+ sp<IBinder> surfaceControlHandle;
+ SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle);
ComposerState composerState;
if (composerState.read(*parcel) == BAD_VALUE) {
return BAD_VALUE;
}
+
composerStates[surfaceControlHandle] = composerState;
}
@@ -451,6 +468,7 @@
mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
+ mFrameTimelineVsyncId = frameTimelineVsyncId;
mDisplayStates = displayStates;
mListenerCallbacks = listenerCallbacks;
mComposerStates = composerStates;
@@ -480,6 +498,7 @@
parcel->writeBool(mExplicitEarlyWakeupEnd);
parcel->writeBool(mContainsBuffer);
parcel->writeInt64(mDesiredPresentTime);
+ parcel->writeInt64(mFrameTimelineVsyncId);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
for (auto const& displayState : mDisplayStates) {
displayState.write(*parcel);
@@ -494,13 +513,13 @@
}
parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
for (auto surfaceControl : callbackInfo.surfaceControls) {
- surfaceControl->writeToParcel(parcel);
+ SAFE_PARCEL(surfaceControl->writeToParcel, *parcel);
}
}
parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
- for (auto const& [surfaceHandle, composerState] : mComposerStates) {
- parcel->writeStrongBinder(surfaceHandle);
+ for (auto const& [handle, composerState] : mComposerStates) {
+ SAFE_PARCEL(parcel->writeStrongBinder, handle);
composerState.write(*parcel);
}
@@ -509,11 +528,11 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
- for (auto const& [surfaceHandle, composerState] : other.mComposerStates) {
- if (mComposerStates.count(surfaceHandle) == 0) {
- mComposerStates[surfaceHandle] = composerState;
+ for (auto const& [handle, composerState] : other.mComposerStates) {
+ if (mComposerStates.count(handle) == 0) {
+ mComposerStates[handle] = composerState;
} else {
- mComposerStates[surfaceHandle].state.merge(composerState.state);
+ mComposerStates[handle].state.merge(composerState.state);
}
}
@@ -555,6 +574,15 @@
mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+
+ // When merging vsync Ids we take the oldest one
+ if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
+ other.mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
+ mFrameTimelineVsyncId = std::max(mFrameTimelineVsyncId, other.mFrameTimelineVsyncId);
+ } else if (mFrameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID) {
+ mFrameTimelineVsyncId = other.mFrameTimelineVsyncId;
+ }
+
other.clear();
return *this;
}
@@ -572,6 +600,7 @@
mExplicitEarlyWakeupStart = false;
mExplicitEarlyWakeupEnd = false;
mDesiredPresentTime = -1;
+ mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -582,7 +611,8 @@
uncacheBuffer.id = cacheId;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
+ sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1,
+ uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -592,7 +622,7 @@
size_t count = 0;
for (auto& [handle, cs] : mComposerStates) {
- layer_state_t* s = getLayerState(handle);
+ layer_state_t* s = &(mComposerStates[handle].state);
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
} else if (s->what & layer_state_t::eCachedBufferChanged) {
@@ -709,11 +739,14 @@
mExplicitEarlyWakeupStart = false;
mExplicitEarlyWakeupEnd = false;
+ uint64_t transactionId = mId;
+ mId = generateId();
+
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
- mDesiredPresentTime,
+ sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
+ mInputWindowCommands, mDesiredPresentTime,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
- hasListenerCallbacks, listenerCallbacks);
+ hasListenerCallbacks, listenerCallbacks, transactionId);
mInputWindowCommands.clear();
mStatus = NO_ERROR;
return NO_ERROR;
@@ -762,11 +795,16 @@
mExplicitEarlyWakeupEnd = true;
}
-layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
+layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
+ auto handle = sc->getHandle();
+
if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
ComposerState s;
+
s.state.surface = handle;
+ s.state.layerId = sc->getLayerId();
+
mComposerStates[handle] = s;
}
@@ -837,8 +875,8 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(const sp<SurfaceControl>& sc, const sp<IBinder>& relativeTo,
- int32_t z) {
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(
+ const sp<SurfaceControl>& sc, const sp<SurfaceControl>& relativeTo, int32_t z) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -846,7 +884,7 @@
}
s->what |= layer_state_t::eRelativeLayerChanged;
s->what &= ~layer_state_t::eLayerChanged;
- s->relativeLayerHandle = relativeTo;
+ s->relativeLayerSurfaceControl = relativeTo;
s->z = z;
registerSurfaceControlForCallback(sc);
@@ -861,9 +899,8 @@
mStatus = BAD_INDEX;
return *this;
}
- if ((mask & layer_state_t::eLayerOpaque) ||
- (mask & layer_state_t::eLayerHidden) ||
- (mask & layer_state_t::eLayerSecure)) {
+ if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
+ (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot)) {
s->what |= layer_state_t::eFlagsChanged;
}
s->flags &= ~mask;
@@ -991,64 +1028,45 @@
}
SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<IBinder>& handle,
- uint64_t frameNumber) {
+SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(
+ const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl,
+ uint64_t frameNumber) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
s->what |= layer_state_t::eDeferTransaction_legacy;
- s->barrierHandle_legacy = handle;
- s->frameNumber_legacy = frameNumber;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
-SurfaceComposerClient::Transaction&
-SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<Surface>& barrierSurface,
- uint64_t frameNumber) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eDeferTransaction_legacy;
- s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
- s->frameNumber_legacy = frameNumber;
+ s->barrierSurfaceControl_legacy = barrierSurfaceControl;
+ s->barrierFrameNumber = frameNumber;
registerSurfaceControlForCallback(sc);
return *this;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren(
- const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle) {
+ const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
s->what |= layer_state_t::eReparentChildren;
- s->reparentHandle = newParentHandle;
+ s->reparentSurfaceControl = newParent;
registerSurfaceControlForCallback(sc);
return *this;
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent(
- const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle) {
+ const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
s->what |= layer_state_t::eReparent;
- s->parentHandleForChild = newParentHandle;
+ s->parentSurfaceControlForChild = newParent;
registerSurfaceControlForCallback(sc);
return *this;
@@ -1308,6 +1326,20 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
+ const sp<SurfaceControl>& sc, uint64_t frameNumber) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eFrameNumberChanged;
+ s->frameNumber = frameNumber;
+
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
const sp<SurfaceControl>& sc) {
layer_state_t* s = getLayerState(sc);
@@ -1365,11 +1397,13 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
- const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos) {
+ const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos,
+ int32_t displayId) {
FocusRequest request;
request.token = token;
request.focusedToken = focusedToken;
request.timestamp = timestampNanos;
+ request.displayId = displayId;
return setFocusedWindow(request);
}
@@ -1499,6 +1533,12 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync(
+ int64_t frameTimelineVsyncId) {
+ mFrameTimelineVsyncId = frameTimelineVsyncId;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1637,14 +1677,16 @@
sp<IGraphicBufferProducer> gbp;
uint32_t transformHint = 0;
+ int32_t id = -1;
err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
- std::move(metadata), &handle, &gbp, &transformHint);
+ std::move(metadata), &handle, &gbp, &id,
+ &transformHint);
if (outTransformHint) {
*outTransformHint = transformHint;
}
ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
if (err == NO_ERROR) {
- return new SurfaceControl(this, handle, gbp, transformHint);
+ return new SurfaceControl(this, handle, gbp, id, transformHint);
}
}
return nullptr;
@@ -1668,14 +1710,16 @@
}
uint32_t transformHint = 0;
+ int32_t id = -1;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
- &handle, &gbp, &transformHint);
+ &handle, &gbp, &id, &transformHint);
+
if (outTransformHint) {
*outTransformHint = transformHint;
}
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
- *outSurface = new SurfaceControl(this, handle, gbp, transformHint);
+ *outSurface = new SurfaceControl(this, handle, gbp, id, transformHint);
}
}
return err;
@@ -1688,9 +1732,10 @@
sp<IBinder> handle;
sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
- status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle);
+ int32_t layer_id = -1;
+ status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle, &layer_id);
if (err == NO_ERROR) {
- return new SurfaceControl(this, handle, nullptr, true /* owned */);
+ return new SurfaceControl(this, handle, nullptr, layer_id, true /* owned */);
}
return nullptr;
}
@@ -1923,24 +1968,27 @@
// ----------------------------------------------------------------------------
status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(captureArgs, captureResults);
+
+ return s->captureDisplay(captureArgs, captureListener);
}
status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(displayOrLayerStack, captureResults);
+
+ return s->captureDisplay(displayOrLayerStack, captureListener);
}
status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureLayers(captureArgs, captureResults);
+
+ return s->captureLayers(captureArgs, captureListener);
}
} // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 8dcb71b..e842382 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -24,6 +24,7 @@
#include <android/native_window.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
#include <utils/Log.h>
#include <utils/threads.h>
@@ -46,11 +47,12 @@
// ============================================================================
SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp,
+ const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
uint32_t transform)
: mClient(client),
mHandle(handle),
mGraphicBufferProducer(gbp),
+ mLayerId(layerId),
mTransformHint(transform) {}
SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
@@ -58,6 +60,7 @@
mHandle = other->mHandle;
mGraphicBufferProducer = other->mGraphicBufferProducer;
mTransformHint = other->mTransformHint;
+ mLayerId = other->mLayerId;
}
SurfaceControl::~SurfaceControl()
@@ -148,6 +151,10 @@
return mHandle;
}
+int32_t SurfaceControl::getLayerId() const {
+ return mLayerId;
+}
+
sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const
{
Mutex::Autolock _l(mLock);
@@ -169,42 +176,60 @@
mTransformHint = hint;
}
-void SurfaceControl::writeToParcel(Parcel* parcel)
-{
- parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
- parcel->writeStrongBinder(mHandle);
- parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
- parcel->writeUint32(mTransformHint);
+status_t SurfaceControl::writeToParcel(Parcel& parcel) {
+ SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient()));
+ SAFE_PARCEL(parcel.writeStrongBinder, mHandle);
+ SAFE_PARCEL(parcel.writeStrongBinder, IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
+ SAFE_PARCEL(parcel.writeInt32, mLayerId);
+ SAFE_PARCEL(parcel.writeUint32, mTransformHint);
+
+ return NO_ERROR;
}
-sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
- bool invalidParcel = false;
- status_t status;
+status_t SurfaceControl::readFromParcel(const Parcel& parcel,
+ sp<SurfaceControl>* outSurfaceControl) {
sp<IBinder> client;
- if ((status = parcel->readStrongBinder(&client)) != OK) {
- ALOGE("Failed to read client: %s", statusToString(status).c_str());
- invalidParcel = true;
- }
sp<IBinder> handle;
- if ((status = parcel->readStrongBinder(&handle)) != OK) {
- ALOGE("Failed to read handle: %s", statusToString(status).c_str());
- invalidParcel = true;
- }
sp<IBinder> gbp;
- if ((status = parcel->readNullableStrongBinder(&gbp)) != OK) {
- ALOGE("Failed to read gbp: %s", statusToString(status).c_str());
- invalidParcel = true;
- }
- uint32_t transformHint = parcel->readUint32();
+ int32_t layerId;
+ uint32_t transformHint;
- if (invalidParcel) {
- return nullptr;
- }
+ SAFE_PARCEL(parcel.readStrongBinder, &client);
+ SAFE_PARCEL(parcel.readStrongBinder, &handle);
+ SAFE_PARCEL(parcel.readNullableStrongBinder, &gbp);
+ SAFE_PARCEL(parcel.readInt32, &layerId);
+ SAFE_PARCEL(parcel.readUint32, &transformHint);
+
// We aren't the original owner of the surface.
- return new SurfaceControl(new SurfaceComposerClient(
- interface_cast<ISurfaceComposerClient>(client)),
- handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+ *outSurfaceControl =
+ new SurfaceControl(new SurfaceComposerClient(
+ interface_cast<ISurfaceComposerClient>(client)),
+ handle.get(), interface_cast<IGraphicBufferProducer>(gbp), layerId,
transformHint);
+
+ return NO_ERROR;
+}
+
+status_t SurfaceControl::readNullableFromParcel(const Parcel& parcel,
+ sp<SurfaceControl>* outSurfaceControl) {
+ bool isNotNull;
+ SAFE_PARCEL(parcel.readBool, &isNotNull);
+ if (isNotNull) {
+ SAFE_PARCEL(SurfaceControl::readFromParcel, parcel, outSurfaceControl);
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceControl::writeNullableToParcel(Parcel& parcel,
+ const sp<SurfaceControl>& surfaceControl) {
+ auto isNotNull = surfaceControl != nullptr;
+ SAFE_PARCEL(parcel.writeBool, isNotNull);
+ if (isNotNull) {
+ SAFE_PARCEL(surfaceControl->writeToParcel, parcel);
+ }
+
+ return NO_ERROR;
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp
index 8df6e81..1a8fc1a 100644
--- a/libs/gui/SyncFeatures.cpp
+++ b/libs/gui/SyncFeatures.cpp
@@ -71,15 +71,7 @@
return mHasNativeFenceSync;
}
bool SyncFeatures::useFenceSync() const {
-#ifdef DONT_USE_FENCE_SYNC
- // on some devices it's better to not use EGL_KHR_fence_sync
- // even if they have it
- return false;
-#else
- // currently we shall only attempt to use EGL_KHR_fence_sync if
- // USE_FENCE_SYNC is set in our makefile
return !mHasNativeFenceSync && mHasFenceSync;
-#endif
}
bool SyncFeatures::useWaitSync() const {
return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync;
diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp
new file mode 100644
index 0000000..eedc3df
--- /dev/null
+++ b/libs/gui/TransactionTracing.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "gui/ISurfaceComposer.h"
+
+#include <private/gui/ComposerService.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<ISurfaceComposer> sf(ComposerService::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
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
new file mode 100644
index 0000000..5cd12fd
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl
@@ -0,0 +1,6 @@
+package android.gui;
+
+/** @hide */
+interface ITransactionTraceListener {
+ void onToggled(boolean enabled);
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 2320771..5b1a018 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -66,8 +66,8 @@
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
public:
- BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
- bool enableTripleBuffering = true);
+ BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+ int height, bool enableTripleBuffering = true);
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
return mProducer;
@@ -81,7 +81,7 @@
const std::vector<SurfaceControlStats>& stats);
void setNextTransaction(SurfaceComposerClient::Transaction *t);
- void update(const sp<SurfaceControl>& surface, int width, int height);
+ void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
virtual ~BLASTBufferQueue() = default;
@@ -94,7 +94,10 @@
void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item);
+ // Return true if we need to reject the buffer based on the scaling mode and the buffer size.
+ bool rejectBuffer(const BufferItem& item) const;
+ std::string mName;
sp<SurfaceControl> mSurfaceControl;
std::mutex mMutex;
@@ -106,17 +109,19 @@
int32_t mNumFrameAvailable GUARDED_BY(mMutex);
int32_t mNumAcquired GUARDED_BY(mMutex);
-
+ bool mInitialCallbackReceived GUARDED_BY(mMutex) = false;
struct PendingReleaseItem {
BufferItem item;
sp<Fence> releaseFence;
};
std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+ // Keep a reference to the currently presented buffer so we can release it when the next buffer
+ // is ready to be presented.
PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
- int mWidth GUARDED_BY(mMutex);
- int mHeight GUARDED_BY(mMutex);
+ uint32_t mWidth GUARDED_BY(mMutex);
+ uint32_t mHeight GUARDED_BY(mMutex);
uint32_t mTransformHint GUARDED_BY(mMutex);
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index eb5b004..cf598ea 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -43,7 +43,8 @@
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
- virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ int64_t vsyncId) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
bool connected) = 0;
virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -53,6 +54,6 @@
virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
- uint32_t* outCount);
+ uint32_t* outCount, int64_t* outVsyncId);
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 7974a06..df3118f 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -72,6 +72,8 @@
struct VSync {
uint32_t count;
nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+ int64_t vsyncId;
};
struct Hotplug {
diff --git a/libs/gui/include/gui/IScreenCaptureListener.h b/libs/gui/include/gui/IScreenCaptureListener.h
new file mode 100644
index 0000000..a2ddc9f
--- /dev/null
+++ b/libs/gui/include/gui/IScreenCaptureListener.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <binder/Binder.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/SafeInterface.h>
+
+namespace android {
+
+struct ScreenCaptureResults;
+
+// TODO(b/166271443): Convert to AIDL
+class IScreenCaptureListener : public IInterface {
+public:
+ DECLARE_META_INTERFACE(ScreenCaptureListener)
+
+ virtual status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) = 0;
+};
+
+class BnScreenCaptureListener : public SafeBnInterface<IScreenCaptureListener> {
+public:
+ BnScreenCaptureListener()
+ : SafeBnInterface<IScreenCaptureListener>("BnScreenCaptureListener") {}
+
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) override;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 926a66f..a416147 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,6 +22,8 @@
#include <binder/IBinder.h>
#include <binder/IInterface.h>
+#include <android/gui/ITransactionTraceListener.h>
+#include <gui/IScreenCaptureListener.h>
#include <gui/ITransactionCompletedListener.h>
#include <math/vec4.h>
@@ -107,6 +109,9 @@
enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
+ // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java
+ static constexpr int64_t INVALID_VSYNC_ID = -1;
+
/*
* Create a connection with SurfaceFlinger.
*/
@@ -150,13 +155,12 @@
}
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
- virtual void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
+ virtual status_t setTransactionState(
+ int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
/* signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
@@ -256,10 +260,10 @@
* match the size of the output buffer.
*/
virtual status_t captureDisplay(const DisplayCaptureArgs& args,
- ScreenCaptureResults& captureResults) = 0;
+ const sp<IScreenCaptureListener>& captureListener) = 0;
virtual status_t captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults) = 0;
+ const sp<IScreenCaptureListener>& captureListener) = 0;
template <class AA>
struct SpHash {
@@ -272,7 +276,7 @@
* is a secure window on screen
*/
virtual status_t captureLayers(const LayerCaptureArgs& args,
- ScreenCaptureResults& captureResults) = 0;
+ const sp<IScreenCaptureListener>& captureListener) = 0;
/* Clears the frame statistics for animations.
*
@@ -479,6 +483,19 @@
* for tests. Release the token by releasing the returned IBinder reference.
*/
virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
+
+ /*
+ * Sets the frame timeline vsync id received from choreographer that corresponds to next
+ * buffer submitted on that surface.
+ */
+ virtual status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+ int64_t frameTimelineVsyncId) = 0;
+
+ /*
+ * Adds a TransactionTraceListener to listen for transaction tracing state updates.
+ */
+ virtual status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) = 0;
};
// ----------------------------------------------------------------------------
@@ -538,6 +555,8 @@
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
+ SET_FRAME_TIMELINE_VSYNC,
+ ADD_TRANSACTION_TRACE_LISTENER,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 3afbabf..4a92f53 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -36,6 +36,7 @@
enum { // (keep in sync with SurfaceControl.java)
eHidden = 0x00000004,
eDestroyBackbuffer = 0x00000020,
+ eSkipScreenshot = 0x00000040,
eSecure = 0x00000080,
eNonPremultiplied = 0x00000100,
eOpaque = 0x00000400,
@@ -57,7 +58,8 @@
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
+ uint32_t* outTransformHint) = 0;
/*
* Requires ACCESS_SURFACE_FLINGER permission
@@ -66,7 +68,7 @@
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint) = 0;
/*
@@ -79,7 +81,8 @@
*/
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
- virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0;
+ virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) = 0;
};
class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 187e478..ff395ec 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -16,6 +16,23 @@
#ifndef ANDROID_SF_LAYER_STATE_H
#define ANDROID_SF_LAYER_STATE_H
+#define SAFE_PARCEL(FUNC, ...) \
+ { \
+ status_t error = FUNC(__VA_ARGS__); \
+ if (error) { \
+ ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \
+ return error; \
+ } \
+ }
+
+#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE) \
+ { \
+ SAFE_PARCEL(FUNC, COUNT); \
+ if (static_cast<unsigned int>(*COUNT) > SIZE) { \
+ ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \
+ return BAD_VALUE; \
+ } \
+ }
#include <stdint.h>
#include <sys/types.h>
@@ -32,6 +49,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerMetadata.h>
+#include <gui/SurfaceControl.h>
#include <math/vec3.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -59,9 +77,10 @@
*/
struct layer_state_t {
enum {
- eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
- eLayerOpaque = 0x02, // SURFACE_OPAQUE
- eLayerSecure = 0x80, // SECURE
+ eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
+ eLayerOpaque = 0x02, // SURFACE_OPAQUE
+ eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT
+ eLayerSecure = 0x80, // SECURE
};
enum {
@@ -107,45 +126,10 @@
eBackgroundBlurRadiusChanged = 0x80'00000000,
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
+ eFrameNumberChanged = 0x400'00000000,
};
- layer_state_t()
- : what(0),
- x(0),
- y(0),
- z(0),
- w(0),
- h(0),
- layerStack(0),
- alpha(0),
- flags(0),
- mask(0),
- reserved(0),
- crop_legacy(Rect::INVALID_RECT),
- cornerRadius(0.0f),
- backgroundBlurRadius(0),
- frameNumber_legacy(0),
- overrideScalingMode(-1),
- transform(0),
- transformToDisplayInverse(false),
- crop(Rect::INVALID_RECT),
- orientedDisplaySpaceRect(Rect::INVALID_RECT),
- dataspace(ui::Dataspace::UNKNOWN),
- surfaceDamageRegion(),
- api(-1),
- colorTransform(mat4()),
- bgColorAlpha(0),
- bgColorDataspace(ui::Dataspace::UNKNOWN),
- colorSpaceAgnostic(false),
- shadowRadius(0.0f),
- frameRateSelectionPriority(-1),
- frameRate(0.0f),
- frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
- fixedTransformHint(ui::Transform::ROT_INVALID) {
- matrix.dsdx = matrix.dtdy = 1.0f;
- matrix.dsdy = matrix.dtdx = 0.0f;
- hdrMetadata.validTypes = 0;
- }
+ layer_state_t();
void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
@@ -156,8 +140,11 @@
float dtdx{0};
float dtdy{0};
float dsdy{0};
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
};
sp<IBinder> surface;
+ int32_t layerId;
uint64_t what;
float x;
float y;
@@ -173,16 +160,14 @@
Rect crop_legacy;
float cornerRadius;
uint32_t backgroundBlurRadius;
- sp<IBinder> barrierHandle_legacy;
- sp<IBinder> reparentHandle;
- uint64_t frameNumber_legacy;
+ sp<SurfaceControl> barrierSurfaceControl_legacy;
+ sp<SurfaceControl> reparentSurfaceControl;
+ uint64_t barrierFrameNumber;
int32_t overrideScalingMode;
- sp<IGraphicBufferProducer> barrierGbp_legacy;
+ sp<SurfaceControl> relativeLayerSurfaceControl;
- sp<IBinder> relativeLayerHandle;
-
- sp<IBinder> parentHandleForChild;
+ sp<SurfaceControl> parentSurfaceControlForChild;
half3 color;
@@ -239,6 +224,10 @@
// a buffer of a different size. -1 means the transform hint is not set,
// otherwise the value will be a valid ui::Rotation.
ui::Transform::RotationFlags fixedTransformHint;
+
+ // Used by BlastBufferQueue to forward the framenumber generated by the
+ // graphics producer.
+ uint64_t frameNumber;
};
struct ComposerState {
@@ -292,9 +281,10 @@
// Merges the passed in commands and returns true if there were any changes.
bool merge(const InputWindowCommands& other);
+ bool empty() const;
void clear();
- void write(Parcel& output) const;
- void read(const Parcel& input);
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
};
static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
@@ -322,6 +312,12 @@
float frameScale{1};
bool captureSecureLayers{false};
int32_t uid{UNSET_UID};
+ // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+ // result will be in the display's colorspace.
+ // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+ // different from SRGB (byte per color), and failed when checking colors in tests.
+ // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
virtual status_t write(Parcel& output) const;
virtual status_t read(const Parcel& input);
@@ -350,6 +346,7 @@
sp<GraphicBuffer> buffer;
bool capturedSecureLayers{false};
ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+ status_t result = NO_ERROR;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 55b4101..a68f2e7 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -265,6 +265,7 @@
int dispatchAddQueueInterceptor(va_list args);
int dispatchAddQueryInterceptor(va_list args);
int dispatchGetLastQueuedBuffer(va_list args);
+ int dispatchSetFrameTimelineVsync(va_list args);
bool transformToDisplayInverse();
protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 1a9710a..277060f 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -339,12 +339,18 @@
};
class Transaction : public Parcelable {
+ private:
+ static std::atomic<uint32_t> idCounter;
+ int64_t generateId();
+
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
- SortedVector<DisplayState > mDisplayStates;
+ SortedVector<DisplayState> mDisplayStates;
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
+ uint64_t mId;
+
uint32_t mForceSynchronous = 0;
uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
@@ -367,20 +373,20 @@
// The desired present time does not affect this ordering.
int64_t mDesiredPresentTime = -1;
+ // The vsync Id provided by Choreographer.getVsyncId
+ int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+
InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
- layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle);
- layer_state_t* getLayerState(const sp<SurfaceControl>& sc) {
- return getLayerState(sc->getHandle());
- }
+ layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
DisplayState& getDisplayState(const sp<IBinder>& token);
void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
public:
- Transaction() = default;
+ Transaction();
virtual ~Transaction() = default;
Transaction(Transaction const& other);
@@ -418,7 +424,7 @@
// If the relative is removed, the Surface will have no layer and be
// invisible, until the next time set(Relative)Layer is called.
Transaction& setRelativeLayer(const sp<SurfaceControl>& sc,
- const sp<IBinder>& relativeTo, int32_t z);
+ const sp<SurfaceControl>& relativeTo, int32_t z);
Transaction& setFlags(const sp<SurfaceControl>& sc,
uint32_t flags, uint32_t mask);
Transaction& setTransparentRegionHint(const sp<SurfaceControl>& sc,
@@ -438,22 +444,16 @@
// by handle is removed, then we will apply this transaction regardless of
// what frame number has been reached.
Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<IBinder>& handle, uint64_t frameNumber);
- // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by
- // Surface instead of Handle. Useful for clients which may not have the
- // SurfaceControl for some of their Surfaces. Otherwise behaves identically.
- Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
- const sp<Surface>& barrierSurface,
+ const sp<SurfaceControl>& barrierSurfaceControl,
uint64_t frameNumber);
// Reparents all children of this layer to the new parent handle.
Transaction& reparentChildren(const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle);
+ const sp<SurfaceControl>& newParent);
/// Reparents the current layer to the new parent handle. The new parent must not be null.
// This can be used instead of reparentChildren if the caller wants to
// only re-parent a specific child.
- Transaction& reparent(const sp<SurfaceControl>& sc,
- const sp<IBinder>& newParentHandle);
+ Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent);
Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color);
@@ -487,6 +487,8 @@
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
+ // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
+ Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
@@ -508,7 +510,7 @@
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
Transaction& setFocusedWindow(const sp<IBinder>& token, const sp<IBinder>& focusedToken,
- nsecs_t timestampNanos);
+ nsecs_t timestampNanos, int32_t displayId);
Transaction& setFocusedWindow(const FocusRequest& request);
Transaction& syncInputWindows();
#endif
@@ -532,6 +534,10 @@
// a buffer of a different size.
Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
+ // Sets the frame timeline vsync id received from choreographer that corresponds
+ // to the transaction.
+ Transaction& setFrameTimelineVsync(int64_t frameTimelineVsyncId);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
@@ -595,14 +601,12 @@
class ScreenshotClient {
public:
- // if cropping isn't required, callers may pass in a default Rect, e.g.:
- // capture(display, producer, Rect(), reqWidth, ...);
static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults);
+ const sp<IScreenCaptureListener>& captureListener);
static status_t captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults);
+ const sp<IScreenCaptureListener>& captureListener);
static status_t captureLayers(const LayerCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults);
+ const sp<IScreenCaptureListener>& captureListener);
};
// ---------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index ac2bbcc..35bdfc1 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -20,7 +20,6 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
@@ -44,8 +43,12 @@
class SurfaceControl : public RefBase
{
public:
- static sp<SurfaceControl> readFromParcel(const Parcel* parcel);
- void writeToParcel(Parcel* parcel);
+ static status_t readFromParcel(const Parcel& parcel, sp<SurfaceControl>* outSurfaceControl);
+ status_t writeToParcel(Parcel& parcel);
+
+ static status_t readNullableFromParcel(const Parcel& parcel,
+ sp<SurfaceControl>* outSurfaceControl);
+ static status_t writeNullableToParcel(Parcel& parcel, const sp<SurfaceControl>& surfaceControl);
static bool isValid(const sp<SurfaceControl>& surface) {
return (surface != nullptr) && surface->isValid();
@@ -70,6 +73,7 @@
sp<Surface> getSurface() const;
sp<Surface> createSurface() const;
sp<IBinder> getHandle() const;
+ int32_t getLayerId() const;
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
@@ -85,7 +89,8 @@
explicit SurfaceControl(const sp<SurfaceControl>& other);
SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0);
+ const sp<IGraphicBufferProducer>& gbp, int32_t layerId,
+ uint32_t transformHint = 0);
private:
// can't be copied
@@ -105,6 +110,7 @@
sp<IGraphicBufferProducer> mGraphicBufferProducer;
mutable Mutex mLock;
mutable sp<Surface> mSurfaceData;
+ int32_t mLayerId;
uint32_t mTransformHint;
};
diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h
new file mode 100644
index 0000000..2857996
--- /dev/null
+++ b/libs/gui/include/gui/SyncScreenCaptureListener.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <gui/SurfaceComposerClient.h>
+#include <future>
+
+namespace android {
+
+class SyncScreenCaptureListener : public BnScreenCaptureListener {
+public:
+ status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+ resultsPromise.set_value(captureResults);
+ return NO_ERROR;
+ }
+
+ ScreenCaptureResults waitForResults() {
+ std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
+ return resultsFuture.get();
+ }
+
+private:
+ std::promise<ScreenCaptureResults> resultsPromise;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h
new file mode 100644
index 0000000..9efba47
--- /dev/null
+++ b/libs/gui/include/gui/TransactionTracing.h
@@ -0,0 +1,41 @@
+/*
+ * 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/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index d88c477..9299721 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -25,6 +25,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/IProducerListener.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <private/gui/ComposerService.h>
#include <ui/DisplayConfig.h>
#include <ui/GraphicBuffer.h>
@@ -43,7 +44,7 @@
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+ mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height);
}
void update(const sp<SurfaceControl>& sc, int width, int height) {
@@ -203,6 +204,20 @@
ASSERT_EQ(false, ::testing::Test::HasFailure());
}
+ static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
+ const auto sf = ComposerService::getComposerService();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureDisplay(captureArgs, captureListener);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
sp<SurfaceComposerClient> mClient;
sp<ISurfaceComposer> mComposer;
@@ -306,7 +321,7 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -383,7 +398,7 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -440,7 +455,7 @@
adapter.waitForCallbacks();
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -475,13 +490,13 @@
IGraphicBufferProducer::QueueBufferOutput qbOutput;
IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
Rect(bufWidth, bufHeight),
- NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
- Fence::NO_FENCE);
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+ tr, Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
adapter.waitForCallbacks();
- ASSERT_EQ(NO_ERROR, mComposer->captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
switch (tr) {
case ui::Transform::ROT_0:
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index 4bcb795..7210910 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -33,6 +33,8 @@
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 24);
CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 287a6f4..483f171 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -47,8 +47,7 @@
using android::os::IInputFlinger;
-namespace android {
-namespace test {
+namespace android::test {
using Transaction = SurfaceComposerClient::Transaction;
@@ -69,12 +68,10 @@
public:
InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
mSurfaceControl = sc;
- std::unique_ptr<InputChannel> clientChannel;
- InputChannel::openInputChannelPair("testchannels", mServerChannel, clientChannel);
- mClientChannel = std::move(clientChannel);
mInputFlinger = getInputFlinger();
- mInputFlinger->registerInputChannel(*mServerChannel);
+ mClientChannel = std::make_shared<InputChannel>();
+ mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
populateInputInfo(width, height);
@@ -156,7 +153,7 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- ~InputSurface() { mInputFlinger->unregisterInputChannel(*mServerChannel); }
+ ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
const sp<SurfaceControl>&)> transactionBody) {
@@ -176,6 +173,13 @@
t.apply(true);
}
+ void requestFocus() {
+ SurfaceComposerClient::Transaction t;
+ t.setFocusedWindow(mInputInfo.token, nullptr, systemTime(SYSTEM_TIME_MONOTONIC),
+ 0 /* displayId */);
+ t.apply(true);
+ }
+
private:
void waitForEventAvailable() {
struct pollfd fd;
@@ -186,7 +190,7 @@
}
void populateInputInfo(int width, int height) {
- mInputInfo.token = mServerChannel->getConnectionToken();
+ mInputInfo.token = mClientChannel->getConnectionToken();
mInputInfo.name = "Test info";
mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
@@ -213,7 +217,6 @@
}
public:
sp<SurfaceControl> mSurfaceControl;
- std::unique_ptr<InputChannel> mServerChannel;
std::shared_ptr<InputChannel> mClientChannel;
sp<IInputFlinger> mInputFlinger;
@@ -281,7 +284,6 @@
TEST_F(InputSurfacesTest, can_receive_input) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
- surface->assertFocusChange(true);
injectTap(101, 101);
@@ -297,12 +299,9 @@
TEST_F(InputSurfacesTest, input_respects_positioning) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
- surface->assertFocusChange(true);
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface2->showAt(200, 200);
- surface->assertFocusChange(false);
- surface2->assertFocusChange(true);
injectTap(201, 201);
surface2->expectTap(1, 1);
@@ -329,16 +328,11 @@
std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
surface->showAt(10, 10);
- surface->assertFocusChange(true);
surface2->showAt(10, 10);
- surface->assertFocusChange(false);
- surface2->assertFocusChange(true);
surface->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
- surface2->assertFocusChange(false);
- surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -346,8 +340,6 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.setLayer(sc, LAYER_BASE + 1);
});
- surface2->assertFocusChange(true);
- surface->assertFocusChange(false);
injectTap(11, 11);
surface2->expectTap(1, 1);
@@ -355,8 +347,6 @@
surface2->doTransaction([](auto &t, auto &sc) {
t.hide(sc);
});
- surface2->assertFocusChange(false);
- surface->assertFocusChange(true);
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -369,12 +359,9 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
- fgSurface->assertFocusChange(true);
- bgSurface->assertFocusChange(false);
injectTap(106, 106);
fgSurface->expectTap(1, 1);
@@ -388,16 +375,13 @@
std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
parentSurface->showAt(100, 100);
- parentSurface->assertFocusChange(true);
childSurface->mInputInfo.surfaceInset = 10;
childSurface->showAt(100, 100);
- childSurface->assertFocusChange(true);
- parentSurface->assertFocusChange(false);
childSurface->doTransaction([&](auto &t, auto &sc) {
t.setPosition(sc, -5, -5);
- t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
+ t.reparent(sc, parentSurface->mSurfaceControl);
});
injectTap(106, 106);
@@ -412,12 +396,9 @@
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(100, 100);
- bgSurface->assertFocusChange(true);
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
- bgSurface->assertFocusChange(false);
- fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
@@ -434,7 +415,6 @@
// In case we pass the very big inset without any checking.
fgSurface->mInputInfo.surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
- fgSurface->assertFocusChange(true);
fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
@@ -451,7 +431,6 @@
t.setTransparentRegionHint(sc, transparentRegion);
});
surface->showAt(100, 100);
- surface->assertFocusChange(true);
injectTap(101, 101);
surface->expectTap(1, 1);
}
@@ -466,10 +445,7 @@
InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
- bgSurface->assertFocusChange(false);
- bufferSurface->assertFocusChange(true);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -486,10 +462,7 @@
postBuffer(bufferSurface->mSurfaceControl);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
bufferSurface->showAt(10, 10);
- bufferSurface->assertFocusChange(true);
- bgSurface->assertFocusChange(false);
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
@@ -505,10 +478,7 @@
std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
fgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(false);
- fgSurface->assertFocusChange(true);
injectTap(11, 11);
fgSurface->expectTap(1, 1);
@@ -525,17 +495,12 @@
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
bgSurface->showAt(10, 10);
- bgSurface->assertFocusChange(true);
containerSurface->showAt(10, 10);
- bgSurface->assertFocusChange(false);
- containerSurface->assertFocusChange(true);
injectTap(11, 11);
containerSurface->expectTap(1, 1);
containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
- containerSurface->assertFocusChange(false);
- bgSurface->assertFocusChange(true);
injectTap(11, 11);
bgSurface->expectTap(1, 1);
@@ -544,7 +509,6 @@
TEST_F(InputSurfacesTest, input_respects_outscreen) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(-1, -1);
- surface->assertFocusChange(true);
injectTap(0, 0);
surface->expectTap(1, 1);
@@ -556,11 +520,86 @@
InputSurface::makeCursorInputSurface(mComposerClient, 10, 10);
surface->showAt(10, 10);
- surface->assertFocusChange(true);
cursorSurface->showAt(10, 10);
injectTap(11, 11);
surface->expectTap(1, 1);
}
+
+TEST_F(InputSurfacesTest, can_be_focused) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+ surface->requestFocus();
+
+ surface->assertFocusChange(true);
}
+
+TEST_F(InputSurfacesTest, rotate_surface) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(10, 10);
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees
+ });
+ injectTap(8, 11);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees
+ });
+ injectTap(9, 8);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees
+ });
+ injectTap(12, 9);
+ surface->expectTap(1, 2);
}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(10, 10);
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+ });
+ injectTap(2, 12);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+ });
+ injectTap(8, 2);
+ surface->expectTap(1, 2);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+ });
+ injectTap(18, 8);
+ surface->expectTap(1, 2);
+}
+
+TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->mInputInfo.surfaceInset = 5;
+ surface->showAt(100, 100);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
+ });
+ injectTap(40, 120);
+ surface->expectTap(5, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
+ });
+ injectTap(80, 40);
+ surface->expectTap(5, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
+ });
+ injectTap(160, 80);
+ surface->expectTap(5, 10);
+}
+
+} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 2f8e412..5a376da 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -28,6 +28,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <inttypes.h>
#include <private/gui/ComposerService.h>
#include <ui/BufferQueueDefs.h>
@@ -197,6 +198,20 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
+ static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
+ const auto sf = ComposerService::getComposerService();
+ SurfaceComposerClient::Transaction().apply(true);
+
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureDisplay(captureArgs, captureListener);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
sp<Surface> mSurface;
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -250,7 +265,7 @@
captureArgs.height = 64;
ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
+ ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
NATIVE_WINDOW_API_CPU));
@@ -280,7 +295,7 @@
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
+ ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
}
TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
@@ -662,8 +677,7 @@
NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr };
};
-
-class FakeSurfaceComposer : public ISurfaceComposer{
+class FakeSurfaceComposer : public ISurfaceComposer {
public:
~FakeSurfaceComposer() override {}
@@ -681,13 +695,17 @@
void destroyDisplay(const sp<IBinder>& /*display */) override {}
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; }
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; }
- void setTransactionState(const Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
- const sp<IBinder>& /*applyToken*/,
- const InputWindowCommands& /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
- bool /*hasListenerCallbacks*/,
- const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
+ status_t setTransactionState(int64_t /*frameTimelineVsyncId*/,
+ const Vector<ComposerState>& /*state*/,
+ const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ const sp<IBinder>& /*applyToken*/,
+ const InputWindowCommands& /*inputWindowCommands*/,
+ int64_t /*desiredPresentTime*/,
+ const client_cache_t& /*cachedBuffer*/,
+ bool /*hasListenerCallbacks*/,
+ const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
+ uint64_t /*transactionId*/) override {
+ return NO_ERROR;
}
void bootFinished() override {}
@@ -743,7 +761,7 @@
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
- ScreenCaptureResults& /* captureResults */) override {
+ const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
@@ -757,11 +775,12 @@
}
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
- ScreenCaptureResults& /* captureResults */) override {
+ const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
- virtual status_t captureLayers(const LayerCaptureArgs& /* captureArgs */,
- ScreenCaptureResults& /* captureResults */) override {
+ virtual status_t captureLayers(
+ const LayerCaptureArgs& /* captureArgs */,
+ const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
status_t clearAnimationFrameStats() override { return NO_ERROR; }
@@ -853,7 +872,19 @@
return NO_ERROR;
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; }
+ status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
+ return NO_ERROR;
+ }
+
+ status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& /*surface*/,
+ int64_t /*frameTimelineVsyncId*/) override {
+ return NO_ERROR;
+ }
+
+ status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& /*listener*/) override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 0c3c1f0..d8c3e6f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -17,7 +17,11 @@
filegroup {
name: "inputconstants_aidl",
srcs: [
+ "android/os/BlockUntrustedTouchesMode.aidl",
"android/os/IInputConstants.aidl",
+ "android/os/InputEventInjectionResult.aidl",
+ "android/os/InputEventInjectionSync.aidl",
+ "android/os/TouchOcclusionMode.aidl",
],
}
@@ -32,9 +36,11 @@
srcs: [
"Input.cpp",
"InputDevice.cpp",
+ "InputEventLabels.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "PropertyMap.cpp",
"TouchVideoFrame.cpp",
"VirtualKeyMap.cpp",
],
@@ -69,10 +75,16 @@
"android/FocusRequest.aidl",
"android/InputApplicationInfo.aidl",
"android/os/IInputConstants.aidl",
+ "android/os/InputEventInjectionResult.aidl",
+ "android/os/InputEventInjectionSync.aidl",
+ "android/os/TouchOcclusionMode.aidl",
+ "android/os/BlockUntrustedTouchesMode.aidl",
"android/os/IInputFlinger.aidl",
"android/os/ISetInputWindowsListener.aidl",
],
+ export_shared_lib_headers: ["libbinder"],
+
shared_libs: [
"libutils",
"libbinder",
@@ -96,4 +108,23 @@
},
}
+cc_defaults {
+ name: "libinput_fuzz_defaults",
+ host_supported: true,
+ shared_libs: [
+ "libutils",
+ "libbase",
+ "liblog",
+ ],
+}
+
+cc_fuzz {
+ name: "libinput_fuzz_propertymap",
+ defaults: ["libinput_fuzz_defaults"],
+ srcs: [
+ "PropertyMap.cpp",
+ "PropertyMap_fuzz.cpp",
+ ],
+}
+
subdirs = ["tests"]
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index fc73de3..fb2f186 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "Input"
//#define LOG_NDEBUG 0
+#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <limits.h>
#include <string.h>
@@ -135,11 +136,11 @@
// --- KeyEvent ---
const char* KeyEvent::getLabel(int32_t keyCode) {
- return getLabelByKeyCode(keyCode);
+ return InputEventLookup::getLabelByKeyCode(keyCode);
}
int32_t KeyEvent::getKeyCodeFromLabel(const char* label) {
- return getKeyCodeByLabel(label);
+ return InputEventLookup::getKeyCodeByLabel(label);
}
void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
@@ -375,7 +376,7 @@
mSamplePointerCoords = other->mSamplePointerCoords;
} else {
mSampleEventTimes.clear();
- mSampleEventTimes.push(other->getEventTime());
+ mSampleEventTimes.push_back(other->getEventTime());
mSamplePointerCoords.clear();
size_t pointerCount = other->getPointerCount();
size_t historySize = other->getHistorySize();
@@ -387,7 +388,7 @@
void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
- mSampleEventTimes.push(eventTime);
+ mSampleEventTimes.push_back(eventTime);
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
@@ -598,7 +599,7 @@
mPointerProperties.clear();
mPointerProperties.setCapacity(pointerCount);
mSampleEventTimes.clear();
- mSampleEventTimes.setCapacity(sampleCount);
+ mSampleEventTimes.reserve(sampleCount);
mSamplePointerCoords.clear();
mSamplePointerCoords.setCapacity(sampleCount * pointerCount);
@@ -611,7 +612,7 @@
while (sampleCount > 0) {
sampleCount--;
- mSampleEventTimes.push(parcel->readInt64());
+ mSampleEventTimes.push_back(parcel->readInt64());
for (size_t i = 0; i < pointerCount; i++) {
mSamplePointerCoords.push();
status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel);
@@ -662,7 +663,7 @@
const PointerCoords* pc = mSamplePointerCoords.array();
for (size_t h = 0; h < sampleCount; h++) {
- parcel->writeInt64(mSampleEventTimes.itemAt(h));
+ parcel->writeInt64(mSampleEventTimes[h]);
for (size_t i = 0; i < pointerCount; i++) {
status_t status = (pc++)->writeToParcel(parcel);
if (status) {
@@ -692,11 +693,11 @@
}
const char* MotionEvent::getLabel(int32_t axis) {
- return getAxisLabel(axis);
+ return InputEventLookup::getAxisLabel(axis);
}
int32_t MotionEvent::getAxisFromLabel(const char* label) {
- return getAxisByLabel(label);
+ return InputEventLookup::getAxisByLabel(label);
}
const char* MotionEvent::actionToString(int32_t action) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index dbd6293..34eba5b 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -153,14 +153,20 @@
initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false);
}
-InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
- mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber),
- mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal),
- mHasMic(other.mHasMic), mSources(other.mSources),
- mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap),
- mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad),
- mMotionRanges(other.mMotionRanges) {
-}
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other)
+ : mId(other.mId),
+ mGeneration(other.mGeneration),
+ mControllerNumber(other.mControllerNumber),
+ mIdentifier(other.mIdentifier),
+ mAlias(other.mAlias),
+ mIsExternal(other.mIsExternal),
+ mHasMic(other.mHasMic),
+ mSources(other.mSources),
+ mKeyboardType(other.mKeyboardType),
+ mKeyCharacterMap(other.mKeyCharacterMap),
+ mHasVibrator(other.mHasVibrator),
+ mHasButtonUnderPad(other.mHasButtonUnderPad),
+ mMotionRanges(other.mMotionRanges) {}
InputDeviceInfo::~InputDeviceInfo() {
}
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
new file mode 100644
index 0000000..c0aa2e2
--- /dev/null
+++ b/libs/input/InputEventLabels.cpp
@@ -0,0 +1,451 @@
+/*
+ * 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 <input/InputEventLabels.h>
+
+#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
+#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis }
+#define DEFINE_LED(led) { #led, ALED_##led }
+#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag }
+
+namespace android {
+
+// NOTE: If you add a new keycode here you must also add it to several other files.
+// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
+#define KEYCODES_SEQUENCE \
+ DEFINE_KEYCODE(UNKNOWN), \
+ DEFINE_KEYCODE(SOFT_LEFT), \
+ DEFINE_KEYCODE(SOFT_RIGHT), \
+ DEFINE_KEYCODE(HOME), \
+ DEFINE_KEYCODE(BACK), \
+ DEFINE_KEYCODE(CALL), \
+ DEFINE_KEYCODE(ENDCALL), \
+ DEFINE_KEYCODE(0), \
+ DEFINE_KEYCODE(1), \
+ DEFINE_KEYCODE(2), \
+ DEFINE_KEYCODE(3), \
+ DEFINE_KEYCODE(4), \
+ DEFINE_KEYCODE(5), \
+ DEFINE_KEYCODE(6), \
+ DEFINE_KEYCODE(7), \
+ DEFINE_KEYCODE(8), \
+ DEFINE_KEYCODE(9), \
+ DEFINE_KEYCODE(STAR), \
+ DEFINE_KEYCODE(POUND), \
+ DEFINE_KEYCODE(DPAD_UP), \
+ DEFINE_KEYCODE(DPAD_DOWN), \
+ DEFINE_KEYCODE(DPAD_LEFT), \
+ DEFINE_KEYCODE(DPAD_RIGHT), \
+ DEFINE_KEYCODE(DPAD_CENTER), \
+ DEFINE_KEYCODE(VOLUME_UP), \
+ DEFINE_KEYCODE(VOLUME_DOWN), \
+ DEFINE_KEYCODE(POWER), \
+ DEFINE_KEYCODE(CAMERA), \
+ DEFINE_KEYCODE(CLEAR), \
+ DEFINE_KEYCODE(A), \
+ DEFINE_KEYCODE(B), \
+ DEFINE_KEYCODE(C), \
+ DEFINE_KEYCODE(D), \
+ DEFINE_KEYCODE(E), \
+ DEFINE_KEYCODE(F), \
+ DEFINE_KEYCODE(G), \
+ DEFINE_KEYCODE(H), \
+ DEFINE_KEYCODE(I), \
+ DEFINE_KEYCODE(J), \
+ DEFINE_KEYCODE(K), \
+ DEFINE_KEYCODE(L), \
+ DEFINE_KEYCODE(M), \
+ DEFINE_KEYCODE(N), \
+ DEFINE_KEYCODE(O), \
+ DEFINE_KEYCODE(P), \
+ DEFINE_KEYCODE(Q), \
+ DEFINE_KEYCODE(R), \
+ DEFINE_KEYCODE(S), \
+ DEFINE_KEYCODE(T), \
+ DEFINE_KEYCODE(U), \
+ DEFINE_KEYCODE(V), \
+ DEFINE_KEYCODE(W), \
+ DEFINE_KEYCODE(X), \
+ DEFINE_KEYCODE(Y), \
+ DEFINE_KEYCODE(Z), \
+ DEFINE_KEYCODE(COMMA), \
+ DEFINE_KEYCODE(PERIOD), \
+ DEFINE_KEYCODE(ALT_LEFT), \
+ DEFINE_KEYCODE(ALT_RIGHT), \
+ DEFINE_KEYCODE(SHIFT_LEFT), \
+ DEFINE_KEYCODE(SHIFT_RIGHT), \
+ DEFINE_KEYCODE(TAB), \
+ DEFINE_KEYCODE(SPACE), \
+ DEFINE_KEYCODE(SYM), \
+ DEFINE_KEYCODE(EXPLORER), \
+ DEFINE_KEYCODE(ENVELOPE), \
+ DEFINE_KEYCODE(ENTER), \
+ DEFINE_KEYCODE(DEL), \
+ DEFINE_KEYCODE(GRAVE), \
+ DEFINE_KEYCODE(MINUS), \
+ DEFINE_KEYCODE(EQUALS), \
+ DEFINE_KEYCODE(LEFT_BRACKET), \
+ DEFINE_KEYCODE(RIGHT_BRACKET), \
+ DEFINE_KEYCODE(BACKSLASH), \
+ DEFINE_KEYCODE(SEMICOLON), \
+ DEFINE_KEYCODE(APOSTROPHE), \
+ DEFINE_KEYCODE(SLASH), \
+ DEFINE_KEYCODE(AT), \
+ DEFINE_KEYCODE(NUM), \
+ DEFINE_KEYCODE(HEADSETHOOK), \
+ DEFINE_KEYCODE(FOCUS), \
+ DEFINE_KEYCODE(PLUS), \
+ DEFINE_KEYCODE(MENU), \
+ DEFINE_KEYCODE(NOTIFICATION), \
+ DEFINE_KEYCODE(SEARCH), \
+ DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \
+ DEFINE_KEYCODE(MEDIA_STOP), \
+ DEFINE_KEYCODE(MEDIA_NEXT), \
+ DEFINE_KEYCODE(MEDIA_PREVIOUS), \
+ DEFINE_KEYCODE(MEDIA_REWIND), \
+ DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \
+ DEFINE_KEYCODE(MUTE), \
+ DEFINE_KEYCODE(PAGE_UP), \
+ DEFINE_KEYCODE(PAGE_DOWN), \
+ DEFINE_KEYCODE(PICTSYMBOLS), \
+ DEFINE_KEYCODE(SWITCH_CHARSET), \
+ DEFINE_KEYCODE(BUTTON_A), \
+ DEFINE_KEYCODE(BUTTON_B), \
+ DEFINE_KEYCODE(BUTTON_C), \
+ DEFINE_KEYCODE(BUTTON_X), \
+ DEFINE_KEYCODE(BUTTON_Y), \
+ DEFINE_KEYCODE(BUTTON_Z), \
+ DEFINE_KEYCODE(BUTTON_L1), \
+ DEFINE_KEYCODE(BUTTON_R1), \
+ DEFINE_KEYCODE(BUTTON_L2), \
+ DEFINE_KEYCODE(BUTTON_R2), \
+ DEFINE_KEYCODE(BUTTON_THUMBL), \
+ DEFINE_KEYCODE(BUTTON_THUMBR), \
+ DEFINE_KEYCODE(BUTTON_START), \
+ DEFINE_KEYCODE(BUTTON_SELECT), \
+ DEFINE_KEYCODE(BUTTON_MODE), \
+ DEFINE_KEYCODE(ESCAPE), \
+ DEFINE_KEYCODE(FORWARD_DEL), \
+ DEFINE_KEYCODE(CTRL_LEFT), \
+ DEFINE_KEYCODE(CTRL_RIGHT), \
+ DEFINE_KEYCODE(CAPS_LOCK), \
+ DEFINE_KEYCODE(SCROLL_LOCK), \
+ DEFINE_KEYCODE(META_LEFT), \
+ DEFINE_KEYCODE(META_RIGHT), \
+ DEFINE_KEYCODE(FUNCTION), \
+ DEFINE_KEYCODE(SYSRQ), \
+ DEFINE_KEYCODE(BREAK), \
+ DEFINE_KEYCODE(MOVE_HOME), \
+ DEFINE_KEYCODE(MOVE_END), \
+ DEFINE_KEYCODE(INSERT), \
+ DEFINE_KEYCODE(FORWARD), \
+ DEFINE_KEYCODE(MEDIA_PLAY), \
+ DEFINE_KEYCODE(MEDIA_PAUSE), \
+ DEFINE_KEYCODE(MEDIA_CLOSE), \
+ DEFINE_KEYCODE(MEDIA_EJECT), \
+ DEFINE_KEYCODE(MEDIA_RECORD), \
+ DEFINE_KEYCODE(F1), \
+ DEFINE_KEYCODE(F2), \
+ DEFINE_KEYCODE(F3), \
+ DEFINE_KEYCODE(F4), \
+ DEFINE_KEYCODE(F5), \
+ DEFINE_KEYCODE(F6), \
+ DEFINE_KEYCODE(F7), \
+ DEFINE_KEYCODE(F8), \
+ DEFINE_KEYCODE(F9), \
+ DEFINE_KEYCODE(F10), \
+ DEFINE_KEYCODE(F11), \
+ DEFINE_KEYCODE(F12), \
+ DEFINE_KEYCODE(NUM_LOCK), \
+ DEFINE_KEYCODE(NUMPAD_0), \
+ DEFINE_KEYCODE(NUMPAD_1), \
+ DEFINE_KEYCODE(NUMPAD_2), \
+ DEFINE_KEYCODE(NUMPAD_3), \
+ DEFINE_KEYCODE(NUMPAD_4), \
+ DEFINE_KEYCODE(NUMPAD_5), \
+ DEFINE_KEYCODE(NUMPAD_6), \
+ DEFINE_KEYCODE(NUMPAD_7), \
+ DEFINE_KEYCODE(NUMPAD_8), \
+ DEFINE_KEYCODE(NUMPAD_9), \
+ DEFINE_KEYCODE(NUMPAD_DIVIDE), \
+ DEFINE_KEYCODE(NUMPAD_MULTIPLY), \
+ DEFINE_KEYCODE(NUMPAD_SUBTRACT), \
+ DEFINE_KEYCODE(NUMPAD_ADD), \
+ DEFINE_KEYCODE(NUMPAD_DOT), \
+ DEFINE_KEYCODE(NUMPAD_COMMA), \
+ DEFINE_KEYCODE(NUMPAD_ENTER), \
+ DEFINE_KEYCODE(NUMPAD_EQUALS), \
+ DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \
+ DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \
+ DEFINE_KEYCODE(VOLUME_MUTE), \
+ DEFINE_KEYCODE(INFO), \
+ DEFINE_KEYCODE(CHANNEL_UP), \
+ DEFINE_KEYCODE(CHANNEL_DOWN), \
+ DEFINE_KEYCODE(ZOOM_IN), \
+ DEFINE_KEYCODE(ZOOM_OUT), \
+ DEFINE_KEYCODE(TV), \
+ DEFINE_KEYCODE(WINDOW), \
+ DEFINE_KEYCODE(GUIDE), \
+ DEFINE_KEYCODE(DVR), \
+ DEFINE_KEYCODE(BOOKMARK), \
+ DEFINE_KEYCODE(CAPTIONS), \
+ DEFINE_KEYCODE(SETTINGS), \
+ DEFINE_KEYCODE(TV_POWER), \
+ DEFINE_KEYCODE(TV_INPUT), \
+ DEFINE_KEYCODE(STB_POWER), \
+ DEFINE_KEYCODE(STB_INPUT), \
+ DEFINE_KEYCODE(AVR_POWER), \
+ DEFINE_KEYCODE(AVR_INPUT), \
+ DEFINE_KEYCODE(PROG_RED), \
+ DEFINE_KEYCODE(PROG_GREEN), \
+ DEFINE_KEYCODE(PROG_YELLOW), \
+ DEFINE_KEYCODE(PROG_BLUE), \
+ DEFINE_KEYCODE(APP_SWITCH), \
+ DEFINE_KEYCODE(BUTTON_1), \
+ DEFINE_KEYCODE(BUTTON_2), \
+ DEFINE_KEYCODE(BUTTON_3), \
+ DEFINE_KEYCODE(BUTTON_4), \
+ DEFINE_KEYCODE(BUTTON_5), \
+ DEFINE_KEYCODE(BUTTON_6), \
+ DEFINE_KEYCODE(BUTTON_7), \
+ DEFINE_KEYCODE(BUTTON_8), \
+ DEFINE_KEYCODE(BUTTON_9), \
+ DEFINE_KEYCODE(BUTTON_10), \
+ DEFINE_KEYCODE(BUTTON_11), \
+ DEFINE_KEYCODE(BUTTON_12), \
+ DEFINE_KEYCODE(BUTTON_13), \
+ DEFINE_KEYCODE(BUTTON_14), \
+ DEFINE_KEYCODE(BUTTON_15), \
+ DEFINE_KEYCODE(BUTTON_16), \
+ DEFINE_KEYCODE(LANGUAGE_SWITCH), \
+ DEFINE_KEYCODE(MANNER_MODE), \
+ DEFINE_KEYCODE(3D_MODE), \
+ DEFINE_KEYCODE(CONTACTS), \
+ DEFINE_KEYCODE(CALENDAR), \
+ DEFINE_KEYCODE(MUSIC), \
+ DEFINE_KEYCODE(CALCULATOR), \
+ DEFINE_KEYCODE(ZENKAKU_HANKAKU), \
+ DEFINE_KEYCODE(EISU), \
+ DEFINE_KEYCODE(MUHENKAN), \
+ DEFINE_KEYCODE(HENKAN), \
+ DEFINE_KEYCODE(KATAKANA_HIRAGANA), \
+ DEFINE_KEYCODE(YEN), \
+ DEFINE_KEYCODE(RO), \
+ DEFINE_KEYCODE(KANA), \
+ DEFINE_KEYCODE(ASSIST), \
+ DEFINE_KEYCODE(BRIGHTNESS_DOWN), \
+ DEFINE_KEYCODE(BRIGHTNESS_UP), \
+ DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \
+ DEFINE_KEYCODE(SLEEP), \
+ DEFINE_KEYCODE(WAKEUP), \
+ DEFINE_KEYCODE(PAIRING), \
+ DEFINE_KEYCODE(MEDIA_TOP_MENU), \
+ DEFINE_KEYCODE(11), \
+ DEFINE_KEYCODE(12), \
+ DEFINE_KEYCODE(LAST_CHANNEL), \
+ DEFINE_KEYCODE(TV_DATA_SERVICE), \
+ DEFINE_KEYCODE(VOICE_ASSIST), \
+ DEFINE_KEYCODE(TV_RADIO_SERVICE), \
+ DEFINE_KEYCODE(TV_TELETEXT), \
+ DEFINE_KEYCODE(TV_NUMBER_ENTRY), \
+ DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \
+ DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \
+ DEFINE_KEYCODE(TV_SATELLITE), \
+ DEFINE_KEYCODE(TV_SATELLITE_BS), \
+ DEFINE_KEYCODE(TV_SATELLITE_CS), \
+ DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \
+ DEFINE_KEYCODE(TV_NETWORK), \
+ DEFINE_KEYCODE(TV_ANTENNA_CABLE), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_1), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_2), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_3), \
+ DEFINE_KEYCODE(TV_INPUT_HDMI_4), \
+ DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \
+ DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \
+ DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \
+ DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \
+ DEFINE_KEYCODE(TV_INPUT_VGA_1), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \
+ DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \
+ DEFINE_KEYCODE(TV_ZOOM_MODE), \
+ DEFINE_KEYCODE(TV_CONTENTS_MENU), \
+ DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \
+ DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \
+ DEFINE_KEYCODE(HELP), \
+ DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \
+ DEFINE_KEYCODE(NAVIGATE_NEXT), \
+ DEFINE_KEYCODE(NAVIGATE_IN), \
+ DEFINE_KEYCODE(NAVIGATE_OUT), \
+ DEFINE_KEYCODE(STEM_PRIMARY), \
+ DEFINE_KEYCODE(STEM_1), \
+ DEFINE_KEYCODE(STEM_2), \
+ DEFINE_KEYCODE(STEM_3), \
+ DEFINE_KEYCODE(DPAD_UP_LEFT), \
+ DEFINE_KEYCODE(DPAD_DOWN_LEFT), \
+ DEFINE_KEYCODE(DPAD_UP_RIGHT), \
+ DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \
+ DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \
+ DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \
+ DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \
+ DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \
+ DEFINE_KEYCODE(SOFT_SLEEP), \
+ DEFINE_KEYCODE(CUT), \
+ DEFINE_KEYCODE(COPY), \
+ DEFINE_KEYCODE(PASTE), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \
+ DEFINE_KEYCODE(ALL_APPS), \
+ DEFINE_KEYCODE(REFRESH), \
+ DEFINE_KEYCODE(THUMBS_UP), \
+ DEFINE_KEYCODE(THUMBS_DOWN), \
+ DEFINE_KEYCODE(PROFILE_SWITCH)
+
+// 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.
+#define AXES_SEQUENCE \
+ DEFINE_AXIS(X), \
+ DEFINE_AXIS(Y), \
+ DEFINE_AXIS(PRESSURE), \
+ DEFINE_AXIS(SIZE), \
+ DEFINE_AXIS(TOUCH_MAJOR), \
+ DEFINE_AXIS(TOUCH_MINOR), \
+ DEFINE_AXIS(TOOL_MAJOR), \
+ DEFINE_AXIS(TOOL_MINOR), \
+ DEFINE_AXIS(ORIENTATION), \
+ DEFINE_AXIS(VSCROLL), \
+ DEFINE_AXIS(HSCROLL), \
+ DEFINE_AXIS(Z), \
+ DEFINE_AXIS(RX), \
+ DEFINE_AXIS(RY), \
+ DEFINE_AXIS(RZ), \
+ DEFINE_AXIS(HAT_X), \
+ DEFINE_AXIS(HAT_Y), \
+ DEFINE_AXIS(LTRIGGER), \
+ DEFINE_AXIS(RTRIGGER), \
+ DEFINE_AXIS(THROTTLE), \
+ DEFINE_AXIS(RUDDER), \
+ DEFINE_AXIS(WHEEL), \
+ DEFINE_AXIS(GAS), \
+ DEFINE_AXIS(BRAKE), \
+ DEFINE_AXIS(DISTANCE), \
+ DEFINE_AXIS(TILT), \
+ DEFINE_AXIS(SCROLL), \
+ DEFINE_AXIS(RELATIVE_X), \
+ DEFINE_AXIS(RELATIVE_Y), \
+ {"RESERVED_29", 29}, \
+ {"RESERVED_30", 30}, \
+ {"RESERVED_31", 31}, \
+ DEFINE_AXIS(GENERIC_1), \
+ DEFINE_AXIS(GENERIC_2), \
+ DEFINE_AXIS(GENERIC_3), \
+ DEFINE_AXIS(GENERIC_4), \
+ DEFINE_AXIS(GENERIC_5), \
+ DEFINE_AXIS(GENERIC_6), \
+ DEFINE_AXIS(GENERIC_7), \
+ DEFINE_AXIS(GENERIC_8), \
+ DEFINE_AXIS(GENERIC_9), \
+ DEFINE_AXIS(GENERIC_10), \
+ DEFINE_AXIS(GENERIC_11), \
+ DEFINE_AXIS(GENERIC_12), \
+ DEFINE_AXIS(GENERIC_13), \
+ DEFINE_AXIS(GENERIC_14), \
+ DEFINE_AXIS(GENERIC_15), \
+ DEFINE_AXIS(GENERIC_16)
+
+
+// NOTE: If you add new LEDs here, you must also add them to Input.h
+#define LEDS_SEQUENCE \
+ DEFINE_LED(NUM_LOCK), \
+ DEFINE_LED(CAPS_LOCK), \
+ DEFINE_LED(SCROLL_LOCK), \
+ DEFINE_LED(COMPOSE), \
+ DEFINE_LED(KANA), \
+ DEFINE_LED(SLEEP), \
+ DEFINE_LED(SUSPEND), \
+ DEFINE_LED(MUTE), \
+ DEFINE_LED(MISC), \
+ DEFINE_LED(MAIL), \
+ DEFINE_LED(CHARGING), \
+ DEFINE_LED(CONTROLLER_1), \
+ DEFINE_LED(CONTROLLER_2), \
+ DEFINE_LED(CONTROLLER_3), \
+ DEFINE_LED(CONTROLLER_4)
+
+#define FLAGS_SEQUENCE \
+ DEFINE_FLAG(VIRTUAL), \
+ DEFINE_FLAG(FUNCTION), \
+ DEFINE_FLAG(GESTURE), \
+ DEFINE_FLAG(WAKE)
+
+// --- InputEventLookup ---
+const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE};
+
+const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE};
+
+const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE};
+
+const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
+
+int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+ const char* literal) {
+ std::string str(literal);
+ auto it = map.find(str);
+ return it != map.end() ? it->second : 0;
+}
+
+const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec,
+ int value) {
+ if (static_cast<size_t>(value) < vec.size()) {
+ return vec[value].literal;
+ }
+ return nullptr;
+}
+
+int32_t InputEventLookup::getKeyCodeByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(KEYCODES, label));
+}
+
+const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
+ if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) {
+ return lookupLabelByValue(KEY_NAMES, keyCode);
+ }
+ return nullptr;
+}
+
+uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) {
+ return uint32_t(lookupValueByLabel(FLAGS, label));
+}
+
+int32_t InputEventLookup::getAxisByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(AXES, label));
+}
+
+const char* InputEventLookup::getAxisLabel(int32_t axisId) {
+ return lookupLabelByValue(AXES_NAMES, axisId);
+}
+
+int32_t InputEventLookup::getLedByLabel(const char* label) {
+ return int32_t(lookupValueByLabel(LEDS, label));
+}
+
+} // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 79e15c1..85df405 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -377,22 +377,16 @@
}
std::unique_ptr<InputChannel> InputChannel::dup() const {
- android::base::unique_fd newFd(::dup(getFd()));
- if (!newFd.ok()) {
- ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
- strerror(errno));
- const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
- // If this process is out of file descriptors, then throwing that might end up exploding
- // on the other side of a binder call, which isn't really helpful.
- // Better to just crash here and hope that the FD leak is slow.
- // Other failures could be client errors, so we still propagate those back to the caller.
- LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
- getName().c_str());
- return nullptr;
- }
+ base::unique_fd newFd(dupFd());
return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
}
+void InputChannel::copyTo(InputChannel& outChannel) const {
+ outChannel.mName = getName();
+ outChannel.mFd = dupFd();
+ outChannel.mToken = getConnectionToken();
+}
+
status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
if (parcel == nullptr) {
ALOGE("%s: Null parcel", __func__);
@@ -415,6 +409,23 @@
return mToken;
}
+base::unique_fd InputChannel::dupFd() const {
+ android::base::unique_fd newFd(::dup(getFd()));
+ if (!newFd.ok()) {
+ ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
+ strerror(errno));
+ const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+ // If this process is out of file descriptors, then throwing that might end up exploding
+ // on the other side of a binder call, which isn't really helpful.
+ // Better to just crash here and hope that the FD leak is slow.
+ // Other failures could be client errors, so we still propagate those back to the caller.
+ LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
+ getName().c_str());
+ return {};
+ }
+ return newFd;
+}
+
// --- InputPublisher ---
InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 885dc9b..8546bbb 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -59,10 +59,11 @@
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
info.visible == visible && info.trustedOverlay == trustedOverlay &&
- info.focusable == focusable && info.hasWallpaper == hasWallpaper &&
- info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
- info.inputFeatures == inputFeatures && info.displayId == displayId &&
- info.portalToDisplayId == portalToDisplayId &&
+ info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.packageName == packageName && info.inputFeatures == inputFeatures &&
+ info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
info.applicationInfo == applicationInfo;
}
@@ -91,6 +92,7 @@
parcel->writeInt32(frameBottom) ?:
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
+ parcel->writeFloat(alpha) ?:
parcel->writeFloat(transform.dsdx()) ?:
parcel->writeFloat(transform.dtdx()) ?:
parcel->writeFloat(transform.tx()) ?:
@@ -102,8 +104,10 @@
parcel->writeBool(hasWallpaper) ?:
parcel->writeBool(paused) ?:
parcel->writeBool(trustedOverlay) ?:
+ parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
parcel->writeInt32(ownerPid) ?:
parcel->writeInt32(ownerUid) ?:
+ parcel->writeUtf8AsUtf16(packageName) ?:
parcel->writeInt32(inputFeatures.get()) ?:
parcel->writeInt32(displayId) ?:
parcel->writeInt32(portalToDisplayId) ?:
@@ -134,6 +138,7 @@
flags = Flags<Flag>(parcel->readInt32());
type = static_cast<Type>(parcel->readInt32());
float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ int32_t touchOcclusionModeInt;
// clang-format off
status = parcel->readInt32(&frameLeft) ?:
parcel->readInt32(&frameTop) ?:
@@ -141,6 +146,7 @@
parcel->readInt32(&frameBottom) ?:
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
+ parcel->readFloat(&alpha) ?:
parcel->readFloat(&dsdx) ?:
parcel->readFloat(&dtdx) ?:
parcel->readFloat(&tx) ?:
@@ -152,14 +158,18 @@
parcel->readBool(&hasWallpaper) ?:
parcel->readBool(&paused) ?:
parcel->readBool(&trustedOverlay) ?:
+ parcel->readInt32(&touchOcclusionModeInt) ?:
parcel->readInt32(&ownerPid) ?:
- parcel->readInt32(&ownerUid);
+ parcel->readInt32(&ownerUid) ?:
+ parcel->readUtf8FromUtf16(&packageName);
// clang-format on
if (status != OK) {
return status;
}
+ touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+
inputFeatures = Flags<Feature>(parcel->readInt32());
status = parcel->readInt32(&displayId) ?:
parcel->readInt32(&portalToDisplayId) ?:
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index cb68165..7ac8a2e 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -24,9 +24,10 @@
#endif
#include <android/keycodes.h>
+#include <attestation/HmacKeyManager.h>
#include <input/InputEventLabels.h>
-#include <input/Keyboard.h>
#include <input/KeyCharacterMap.h>
+#include <input/Keyboard.h>
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -85,15 +86,14 @@
// --- KeyCharacterMap ---
-sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
-
KeyCharacterMap::KeyCharacterMap() :
mType(KEYBOARD_TYPE_UNKNOWN) {
}
-KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
- RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
- mKeysByUsageCode(other.mKeysByUsageCode) {
+KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
+ : mType(other.mType),
+ mKeysByScanCode(other.mKeysByScanCode),
+ mKeysByUsageCode(other.mKeysByUsageCode) {
for (size_t i = 0; i < other.mKeys.size(); i++) {
mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
}
@@ -106,104 +106,95 @@
}
}
-status_t KeyCharacterMap::load(const std::string& filename,
- Format format, sp<KeyCharacterMap>* outMap) {
- outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
+ Format format) {
Tokenizer* tokenizer;
status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
- ALOGE("Error %d opening key character map file %s.", status, filename.c_str());
- } else {
- status = load(tokenizer, format, outMap);
- delete tokenizer;
+ return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
}
- return status;
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get(), format);
+ if (ret) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
}
-status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents,
- Format format, sp<KeyCharacterMap>* outMap) {
- outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
+ const std::string& filename, const char* contents, Format format) {
Tokenizer* tokenizer;
status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
if (status) {
ALOGE("Error %d opening key character map.", status);
- } else {
- status = load(tokenizer, format, outMap);
- delete tokenizer;
+ return Errorf("Error {} opening key character map.", status);
}
- return status;
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get(), format);
+ if (ret) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
}
-status_t KeyCharacterMap::load(Tokenizer* tokenizer,
- Format format, sp<KeyCharacterMap>* outMap) {
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer,
+ Format format) {
status_t status = OK;
- sp<KeyCharacterMap> map = new KeyCharacterMap();
+ std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
if (!map.get()) {
ALOGE("Error allocating key character map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map.get(), tokenizer, format);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (!status) {
- *outMap = map;
- }
+ return Errorf("Error allocating key character map.");
}
- return status;
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map.get(), tokenizer, format);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+#endif
+ if (status == OK) {
+ return map;
+ }
+
+ return Errorf("Load KeyCharacterMap failed {}.", status);
}
-sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
- const sp<KeyCharacterMap>& overlay) {
- if (overlay == nullptr) {
- return base;
- }
- if (base == nullptr) {
- return overlay;
- }
-
- sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
- for (size_t i = 0; i < overlay->mKeys.size(); i++) {
- int32_t keyCode = overlay->mKeys.keyAt(i);
- Key* key = overlay->mKeys.valueAt(i);
- ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
+void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
+ for (size_t i = 0; i < overlay.mKeys.size(); i++) {
+ int32_t keyCode = overlay.mKeys.keyAt(i);
+ Key* key = overlay.mKeys.valueAt(i);
+ ssize_t oldIndex = mKeys.indexOfKey(keyCode);
if (oldIndex >= 0) {
- delete map->mKeys.valueAt(oldIndex);
- map->mKeys.editValueAt(oldIndex) = new Key(*key);
+ delete mKeys.valueAt(oldIndex);
+ mKeys.editValueAt(oldIndex) = new Key(*key);
} else {
- map->mKeys.add(keyCode, new Key(*key));
+ mKeys.add(keyCode, new Key(*key));
}
}
- for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
- map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
- overlay->mKeysByScanCode.valueAt(i));
+ for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
+ mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
+ overlay.mKeysByScanCode.valueAt(i));
}
- for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
- map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
- overlay->mKeysByUsageCode.valueAt(i));
+ for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
+ mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
+ overlay.mKeysByUsageCode.valueAt(i));
}
- return map;
-}
-
-sp<KeyCharacterMap> KeyCharacterMap::empty() {
- return sEmpty;
+ mLoadFileName = overlay.mLoadFileName;
}
int32_t KeyCharacterMap::getKeyboardType() const {
return mType;
}
+const std::string KeyCharacterMap::getLoadFileName() const {
+ return mLoadFileName;
+}
+
char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
char16_t result = 0;
const Key* key;
@@ -600,8 +591,12 @@
}
#ifdef __ANDROID__
-sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
- sp<KeyCharacterMap> map = new KeyCharacterMap();
+std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return nullptr;
+ }
+ std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
map->mType = parcel->readInt32();
size_t numKeys = parcel->readInt32();
if (parcel->errorCheck()) {
@@ -656,6 +651,10 @@
}
void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return;
+ }
parcel->writeInt32(mType);
size_t numKeys = mKeys.size();
@@ -880,7 +879,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -897,7 +896,7 @@
status_t KeyCharacterMap::Parser::parseKey() {
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -1017,7 +1016,7 @@
} else if (token == "fallback") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(token.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
if (!keyCode) {
ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
mTokenizer->getLocation().string(),
@@ -1034,7 +1033,7 @@
} else if (token == "replace") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(token.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
if (!keyCode) {
ALOGE("%s: Invalid key code label for replace, got '%s'.",
mTokenizer->getLocation().string(),
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index efca68d..16ce48a 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -49,37 +49,60 @@
KeyLayoutMap::~KeyLayoutMap() {
}
-status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
- outMap->clear();
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
+ const char* contents) {
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening key layout map.", status);
+ return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
+ }
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get());
+ if (ret) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
+}
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) {
Tokenizer* tokenizer;
status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
if (status) {
ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
- } else {
- sp<KeyLayoutMap> map = new KeyLayoutMap();
- if (!map.get()) {
- ALOGE("Error allocating key layout map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map.get(), tokenizer);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (!status) {
- *outMap = map;
- }
- }
- delete tokenizer;
+ return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
}
- return status;
+ std::unique_ptr<Tokenizer> t(tokenizer);
+ auto ret = load(t.get());
+ if (ret) {
+ (*ret)->mLoadFileName = filename;
+ }
+ return ret;
+}
+
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokenizer) {
+ std::shared_ptr<KeyLayoutMap> map = std::shared_ptr<KeyLayoutMap>(new KeyLayoutMap());
+ status_t status = OK;
+ if (!map.get()) {
+ ALOGE("Error allocating key layout map.");
+ return Errorf("Error allocating key layout map.");
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map.get(), tokenizer);
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
+ tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ elapsedTime / 1000000.0);
+#endif
+ if (!status) {
+ return std::move(map);
+ }
+ }
+ return Errorf("Load KeyLayoutMap failed {}.", status);
}
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
@@ -264,7 +287,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string());
+ int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -277,7 +300,7 @@
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
- uint32_t flag = getKeyFlagByLabel(flagToken.string());
+ uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
if (!flag) {
ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
@@ -326,7 +349,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = getAxisByLabel(axisToken.string());
+ axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected inverted axis label, got '%s'.",
mTokenizer->getLocation().string(), axisToken.string());
@@ -346,7 +369,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = getAxisByLabel(lowAxisToken.string());
+ axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected low axis label, got '%s'.",
mTokenizer->getLocation().string(), lowAxisToken.string());
@@ -355,14 +378,14 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
+ axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
if (axisInfo.highAxis < 0) {
ALOGE("%s: Expected high axis label, got '%s'.",
mTokenizer->getLocation().string(), highAxisToken.string());
return BAD_VALUE;
}
} else {
- axisInfo.axis = getAxisByLabel(token.string());
+ axisInfo.axis = InputEventLookup::getAxisByLabel(token.string());
if (axisInfo.axis < 0) {
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
mTokenizer->getLocation().string(), token.string());
@@ -428,7 +451,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t ledCode = getLedByLabel(ledCodeToken.string());
+ int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
if (ledCode < 0) {
ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
ledCodeToken.string());
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 25025f2..38a68b3 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -110,11 +110,11 @@
return NAME_NOT_FOUND;
}
- status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
- if (status) {
- return status;
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+ if (!ret) {
+ return ret.error().code();
}
-
+ keyLayoutMap = *ret;
keyLayoutFile = path;
return OK;
}
@@ -127,12 +127,12 @@
return NAME_NOT_FOUND;
}
- status_t status = KeyCharacterMap::load(path,
- KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
- if (status) {
- return status;
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::load(path, KeyCharacterMap::FORMAT_BASE);
+ if (!ret) {
+ return ret.error().code();
}
-
+ keyCharacterMap = *ret;
keyCharacterMapFile = path;
return OK;
}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
new file mode 100644
index 0000000..a842166
--- /dev/null
+++ b/libs/input/PropertyMap.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "PropertyMap"
+
+#include <input/PropertyMap.h>
+
+// Enables debug output for the parser.
+#define DEBUG_PARSER 0
+
+// Enables debug output for parser performance.
+#define DEBUG_PARSER_PERFORMANCE 0
+
+namespace android {
+
+static const char* WHITESPACE = " \t\r";
+static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
+
+// --- PropertyMap ---
+
+PropertyMap::PropertyMap() {}
+
+PropertyMap::~PropertyMap() {}
+
+void PropertyMap::clear() {
+ mProperties.clear();
+}
+
+void PropertyMap::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool PropertyMap::hasProperty(const String8& key) const {
+ return mProperties.indexOfKey(key) >= 0;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
+ int32_t intValue;
+ if (!tryGetProperty(key, intValue)) {
+ return false;
+ }
+
+ outValue = intValue;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), &end, 10);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(),
+ stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), &end);
+ if (*end != '\0') {
+ ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(),
+ stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+void PropertyMap::addAll(const PropertyMap* map) {
+ for (size_t i = 0; i < map->mProperties.size(); i++) {
+ mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
+ }
+}
+
+android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) {
+ std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>();
+ if (outMap == nullptr) {
+ return android::base::Error(NO_MEMORY) << "Error allocating property map.";
+ }
+
+ 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 std::move(outMap);
+}
+
+// --- PropertyMap::Parser ---
+
+PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer)
+ : mMap(map), mTokenizer(tokenizer) {}
+
+PropertyMap::Parser::~Parser() {}
+
+status_t PropertyMap::Parser::parse() {
+ while (!mTokenizer->isEof()) {
+#if DEBUG_PARSER
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+#endif
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
+ String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
+ if (keyToken.isEmpty()) {
+ ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ if (mTokenizer->nextChar() != '=') {
+ ALOGE("%s: Expected '=' between property key and value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+
+ String8 valueToken = mTokenizer->nextToken(WHITESPACE);
+ if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
+ ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
+ mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+
+ mTokenizer->skipDelimiters(WHITESPACE);
+ if (!mTokenizer->isEol()) {
+ ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(),
+ mTokenizer->peekRemainderOfLine().string());
+ return BAD_VALUE;
+ }
+
+ if (mMap->hasProperty(keyToken)) {
+ ALOGE("%s: Duplicate property value for key '%s'.",
+ mTokenizer->getLocation().string(), keyToken.string());
+ return BAD_VALUE;
+ }
+
+ mMap->addProperty(keyToken, valueToken);
+ }
+
+ mTokenizer->nextLine();
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp
new file mode 100755
index 0000000..afb97a1
--- /dev/null
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "input/PropertyMap.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_FILE_SIZE = 256;
+static constexpr int MAX_STR_LEN = 2048;
+static constexpr int MAX_OPERATIONS = 1000;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+ operations = {
+ [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ propertyMap.getProperties();
+ },
+ [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ propertyMap.clear();
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ propertyMap.hasProperty(key);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ android::String8 out;
+ propertyMap.tryGetProperty(key, out);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void {
+ TemporaryFile tf;
+ // Generate file contents
+ std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
+ // If we have string contents, dump them into the file.
+ // Otherwise, just leave it as an empty file.
+ if (contents.length() > 0) {
+ const char* bytes = contents.c_str();
+ android::base::WriteStringToFd(bytes, tf.fd);
+ }
+ android::PropertyMap::load(tf.path);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ android::String8 val = android::String8(valStr.c_str());
+ propertyMap.addProperty(key, val);
+ },
+};
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::PropertyMap propertyMap = android::PropertyMap();
+
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ operations[op](&dataProvider, propertyMap);
+ }
+ return 0;
+}
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index bcf55b0..2c04d42 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -66,7 +66,7 @@
if (deltaY) {
mRawPosition.y += *deltaY;
}
- mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
+ mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
float vx, vy;
float scale = mParameters.scale;
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 7c28ac5..a44f0b7 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -193,7 +193,11 @@
mStrategy->clearPointers(idBits);
}
-void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
+void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
+ LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
+ "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+ idBits.count(), positions.size());
while (idBits.count() > MAX_POINTERS) {
idBits.clearLastMarkedBit();
}
@@ -285,12 +289,12 @@
pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
}
- nsecs_t eventTime;
- Position positions[pointerCount];
+ std::vector<Position> positions;
+ positions.resize(pointerCount);
size_t historySize = event->getHistorySize();
- for (size_t h = 0; h < historySize; h++) {
- eventTime = event->getHistoricalEventTime(h);
+ for (size_t h = 0; h <= historySize; h++) {
+ nsecs_t eventTime = event->getHistoricalEventTime(h);
for (size_t i = 0; i < pointerCount; i++) {
uint32_t index = pointerIndex[i];
positions[index].x = event->getHistoricalX(i, h);
@@ -298,14 +302,6 @@
}
addMovement(eventTime, idBits, positions);
}
-
- eventTime = event->getEventTime();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t index = pointerIndex[i];
- positions[index].x = event->getX(i);
- positions[index].y = event->getY(i);
- }
- addMovement(eventTime, idBits, positions);
}
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
@@ -346,8 +342,9 @@
mMovements[mIndex].idBits = remainingIdBits;
}
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
if (mMovements[mIndex].eventTime != eventTime) {
// When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
// of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -419,13 +416,15 @@
* http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
* http://en.wikipedia.org/wiki/Gram-Schmidt
*/
-static bool solveLeastSquares(const float* x, const float* y,
- const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) {
+static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
+ const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
+ const size_t m = x.size();
#if DEBUG_STRATEGY
ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
vectorToString(w, m).c_str());
#endif
+ LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
float a[n][m]; // column-major order
@@ -542,7 +541,9 @@
* the default implementation
*/
static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2(
- const float* x, const float* y, size_t count) {
+ const std::vector<float>& x, const std::vector<float>& y) {
+ const size_t count = x.size();
+ LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes");
// Solving y = a*x^2 + b*x + c
float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
@@ -594,11 +595,11 @@
outEstimator->clear();
// Iterate over movement samples in reverse time order and collect samples.
- float x[HISTORY_SIZE];
- float y[HISTORY_SIZE];
- float w[HISTORY_SIZE];
- float time[HISTORY_SIZE];
- uint32_t m = 0;
+ std::vector<float> x;
+ std::vector<float> y;
+ std::vector<float> w;
+ std::vector<float> time;
+
uint32_t index = mIndex;
const Movement& newestMovement = mMovements[mIndex];
do {
@@ -613,13 +614,14 @@
}
const VelocityTracker::Position& position = movement.getPosition(id);
- x[m] = position.x;
- y[m] = position.y;
- w[m] = chooseWeight(index);
- time[m] = -age * 0.000000001f;
+ x.push_back(position.x);
+ y.push_back(position.y);
+ w.push_back(chooseWeight(index));
+ time.push_back(-age * 0.000000001f);
index = (index == 0 ? HISTORY_SIZE : index) - 1;
- } while (++m < HISTORY_SIZE);
+ } while (x.size() < HISTORY_SIZE);
+ const size_t m = x.size();
if (m == 0) {
return false; // no data
}
@@ -632,8 +634,8 @@
if (degree == 2 && mWeighting == WEIGHTING_NONE) {
// Optimize unweighted, quadratic polynomial fit
- std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m);
- std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m);
+ std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x);
+ std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y);
if (xCoeff && yCoeff) {
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2;
@@ -648,8 +650,8 @@
// General case for an Nth degree polynomial fit
float xdet, ydet;
uint32_t n = degree + 1;
- if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
- && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) {
+ if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) &&
+ solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) {
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
@@ -758,8 +760,9 @@
mPointerIdBits.value &= ~idBits.value;
}
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
uint32_t index = 0;
for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
uint32_t id = iterIdBits.clearFirstMarkedBit();
@@ -876,8 +879,9 @@
mMovements[mIndex].idBits = remainingIdBits;
}
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void LegacyVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
if (++mIndex == HISTORY_SIZE) {
mIndex = 0;
}
@@ -990,8 +994,9 @@
mMovements[mIndex].idBits = remainingIdBits;
}
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
- const VelocityTracker::Position* positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(
+ nsecs_t eventTime, BitSet32 idBits,
+ const std::vector<VelocityTracker::Position>& positions) {
if (mMovements[mIndex].eventTime != eventTime) {
// When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
// of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl
index a5034a4..303dd1c 100644
--- a/libs/input/android/FocusRequest.aidl
+++ b/libs/input/android/FocusRequest.aidl
@@ -36,4 +36,8 @@
* from another source such as pointer down.
*/
long timestamp;
+ /**
+ * Display id associated with this request.
+ */
+ int displayId;
}
diff --git a/libs/input/android/os/BlockUntrustedTouchesMode.aidl b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
new file mode 100644
index 0000000..9504e99
--- /dev/null
+++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
@@ -0,0 +1,35 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+
+/**
+ * Block untrusted touches feature mode.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum BlockUntrustedTouchesMode {
+ /** Feature is off. */
+ DISABLED,
+
+ /** Untrusted touches are flagged but not blocked. */
+ PERMISSIVE,
+
+ /** Untrusted touches are blocked. */
+ BLOCK
+}
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 5eefad3..1771d19 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -30,8 +30,8 @@
// shouldn't be a concern.
oneway void setInputWindows(in InputWindowInfo[] inputHandles,
in @nullable ISetInputWindowsListener setInputWindowsListener);
- void registerInputChannel(in InputChannel channel);
- void unregisterInputChannel(in InputChannel channel);
+ InputChannel createInputChannel(in @utf8InCpp String name);
+ void removeInputChannel(in IBinder connectionToken);
/**
* Sets focus to the window identified by the token. This must be called
* after updating any input window handles.
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
new file mode 100644
index 0000000..34f10ec
--- /dev/null
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Constants used to report the outcome of input event injection.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputEventInjectionResult {
+ /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+ PENDING = -1,
+
+ /* Injection succeeded. */
+ SUCCEEDED = 0,
+
+ /* Injection failed because the injector did not have permission to inject
+ * into the application with input focus. */
+ PERMISSION_DENIED = 1,
+
+ /* Injection failed because there were no available input targets. */
+ FAILED = 2,
+
+ /* Injection failed due to a timeout. */
+ TIMED_OUT = 3,
+}
diff --git a/libs/input/android/os/InputEventInjectionSync.aidl b/libs/input/android/os/InputEventInjectionSync.aidl
new file mode 100644
index 0000000..95d24cb
--- /dev/null
+++ b/libs/input/android/os/InputEventInjectionSync.aidl
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * Constants used to specify the input event injection synchronization mode.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputEventInjectionSync {
+ /* Injection is asynchronous and is assumed always to be successful. */
+ NONE = 0,
+
+ /* Waits for previous events to be dispatched so that the input dispatcher can determine
+ * whether input event injection willbe permitted based on the current input focus.
+ * Does not wait for the input event to finish processing. */
+ WAIT_FOR_RESULT = 1,
+
+ /* Waits for the input event to be completely processed. */
+ WAIT_FOR_FINISHED = 2,
+}
diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl
new file mode 100644
index 0000000..106f159
--- /dev/null
+++ b/libs/input/android/os/TouchOcclusionMode.aidl
@@ -0,0 +1,47 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+
+/**
+ * Touch occlusion modes: These modes represent how windows are taken into
+ * consideration in order to decide whether to block obscured touches or
+ * not.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum TouchOcclusionMode {
+ /**
+ * Touches that pass through this window will be blocked if they are
+ * consumed by a different UID and this window is not trusted.
+ */
+ BLOCK_UNTRUSTED,
+
+ /**
+ * The window's opacity will be taken into consideration for touch
+ * occlusion rules if the touch passes through it and the window is not
+ * trusted.
+ */
+ USE_OPACITY,
+
+ /**
+ * The window won't count for touch occlusion rules if the touch passes
+ * through it.
+ */
+ ALLOW
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 9782c1a..44147a5 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,6 +2,7 @@
cc_test {
name: "libinput_tests",
srcs: [
+ "NamedEnum_test.cpp",
"Flags_test.cpp",
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
@@ -19,13 +20,16 @@
"-Wextra",
"-Werror",
],
- shared_libs: [
+ static_libs: [
"libinput",
- "libcutils",
- "libutils",
- "libbinder",
- "libui",
+ ],
+ shared_libs: [
"libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libui",
+ "libutils",
]
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 069bc0e..601d8da 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -17,6 +17,7 @@
#include <array>
#include <math.h>
+#include <attestation/HmacKeyManager.h>
#include <binder/Parcel.h>
#include <gtest/gtest.h>
#include <input/Input.h>
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index e1f2562..1452745 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -20,11 +20,12 @@
#include <sys/mman.h>
#include <time.h>
+#include <attestation/HmacKeyManager.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
#include <input/InputTransport.h>
-#include <utils/Timers.h>
#include <utils/StopWatch.h>
+#include <utils/Timers.h>
namespace android {
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 65a7761..c18a17f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -53,13 +53,16 @@
i.frameBottom = 19;
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
+ i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
i.paused = false;
+ i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
i.ownerPid = 19;
i.ownerUid = 24;
+ i.packageName = "com.example.package";
i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
i.displayId = 34;
i.portalToDisplayId = 2;
@@ -86,13 +89,16 @@
ASSERT_EQ(i.frameBottom, i2.frameBottom);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+ ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
ASSERT_EQ(i.paused, i2.paused);
+ ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode);
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
+ ASSERT_EQ(i.packageName, i2.packageName);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
ASSERT_EQ(i.displayId, i2.displayId);
ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/input/tests/NamedEnum_test.cpp
new file mode 100644
index 0000000..4e93f71
--- /dev/null
+++ b/libs/input/tests/NamedEnum_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <input/NamedEnum.h>
+
+namespace android {
+
+// Test enum class maximum enum value smaller than default maximum of 8.
+enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
+// Big enum contains enum values greater than default maximum of 8.
+enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
+
+// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
+template <>
+constexpr size_t NamedEnum::max<TestBigEnums> = 16;
+
+namespace test {
+using android::TestBigEnums;
+using android::TestEnums;
+
+TEST(NamedEnum, RuntimeNamedEnum) {
+ TestEnums e = TestEnums::ZERO;
+ ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
+
+ e = TestEnums::ONE;
+ ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
+
+ e = TestEnums::THREE;
+ ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
+
+ e = TestEnums::SEVEN;
+ ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
+}
+
+// Test big enum
+TEST(NamedEnum, RuntimeBigNamedEnum) {
+ TestBigEnums e = TestBigEnums::ZERO;
+ ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
+
+ e = TestBigEnums::FIFTEEN;
+ ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
+}
+
+TEST(NamedEnum, RuntimeNamedEnumAsString) {
+ TestEnums e = TestEnums::ZERO;
+ ASSERT_EQ(NamedEnum::string(e), "ZERO");
+
+ e = TestEnums::ONE;
+ ASSERT_EQ(NamedEnum::string(e), "ONE");
+
+ e = TestEnums::THREE;
+ ASSERT_EQ(NamedEnum::string(e), "THREE");
+
+ e = TestEnums::SEVEN;
+ ASSERT_EQ(NamedEnum::string(e), "SEVEN");
+}
+
+TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
+ TestBigEnums e = TestBigEnums::ZERO;
+ ASSERT_EQ(NamedEnum::string(e), "ZERO");
+
+ e = TestBigEnums::FIFTEEN;
+ ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
+}
+
+TEST(NamedEnum, RuntimeUnknownNamedEnum) {
+ TestEnums e = static_cast<TestEnums>(0x5);
+ ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
+ e = static_cast<TestEnums>(0x9);
+ ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
+}
+
+TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
+ TestEnums e = static_cast<TestEnums>(0x5);
+ ASSERT_EQ(NamedEnum::string(e), "0x00000005");
+ e = static_cast<TestEnums>(0x9);
+ ASSERT_EQ(NamedEnum::string(e), "0x00000009");
+}
+
+TEST(NamedEnum, CompileTimeFlagName) {
+ static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
+ static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
+}
+
+} // namespace test
+
+} // namespace android
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index e7db4b0..d049d05 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -21,6 +21,7 @@
#include <math.h>
#include <android-base/stringprintf.h>
+#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <input/VelocityTracker.h>
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 21cfe8c..36f87b8 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <input/Input.h>
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index ff1b5e6..470f28f 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -128,11 +128,14 @@
static Choreographer* getForThread();
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+ int64_t getVsyncId() const;
+
private:
Choreographer(const Choreographer&) = delete;
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ int64_t vsyncId) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
nsecs_t vsyncPeriod) override;
@@ -146,6 +149,7 @@
std::vector<RefreshRateCallback> mRefreshRateCallbacks;
nsecs_t mLatestVsyncPeriod = -1;
+ int64_t mLastVsyncId = -1;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
@@ -350,7 +354,7 @@
// 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) {
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, int64_t vsyncId) {
std::vector<FrameCallback> callbacks{};
{
std::lock_guard<std::mutex> _l{mLock};
@@ -360,6 +364,7 @@
mFrameCallbacks.pop();
}
}
+ mLastVsyncId = vsyncId;
for (const auto& cb : callbacks) {
if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
@@ -404,6 +409,10 @@
}
}
+int64_t Choreographer::getVsyncId() const {
+ return mLastVsyncId;
+}
+
} // namespace android
using namespace android;
@@ -411,6 +420,11 @@
return reinterpret_cast<Choreographer*>(choreographer);
}
+static inline const Choreographer* AChoreographer_to_Choreographer(
+ const AChoreographer* choreographer) {
+ return reinterpret_cast<const Choreographer*>(choreographer);
+}
+
// Glue for private C api
namespace android {
void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -441,12 +455,18 @@
}
void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AChoreographer_postFrameCallback(choreographer, callback, data);
+#pragma clang diagnostic pop
}
void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+#pragma clang diagnostic pop
}
void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) {
@@ -468,15 +488,14 @@
return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
}
+int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
+ return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
+}
+
} // namespace android
/* Glue for the NDK interface */
-static inline const Choreographer* AChoreographer_to_Choreographer(
- const AChoreographer* choreographer) {
- return reinterpret_cast<const Choreographer*>(choreographer);
-}
-
static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
return reinterpret_cast<AChoreographer*>(choreographer);
}
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 2164930..1d57c15 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -29,6 +29,12 @@
// for consumption by callbacks.
void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
+// Returns the vsync id of the last frame callback. Client are expected to call
+// this function from their frame callback function to get the vsyncId and pass
+// it together with a buffer or transaction to the Surface Composer. Calling
+// this function from anywhere else will return an undefined value.
+int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
+
// Trampoline functions allowing libandroid.so to define the NDK symbols without including
// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
// maintains global state, libnativedisplay can never be directly statically
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index fc59431..5ed2e49 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -29,6 +29,7 @@
android::AChoreographer_routeRegisterRefreshRateCallback*;
android::AChoreographer_routeUnregisterRefreshRateCallback*;
android::AChoreographer_signalRefreshRateCallbacks*;
+ android::AChoreographer_getVsyncId*;
android::ADisplay_acquirePhysicalDisplays*;
android::ADisplay_release*;
android::ADisplay_getMaxSupportedFps*;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 16afc68..365e788 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -51,7 +51,15 @@
}
int slot = item.mSlot;
+ *outQueueEmpty = false;
if (item.mFence->isValid()) {
+ // If fence is not signaled, that means frame is not ready and
+ // outQueueEmpty is set to true. By the time the fence is signaled,
+ // there may be a new buffer queued. This is a proper detection for an
+ // empty queue and it is needed to avoid infinite loop in
+ // ASurfaceTexture_dequeueBuffer (see b/159921224).
+ *outQueueEmpty = item.mFence->getStatus() == Fence::Status::Unsignaled;
+
// Wait on the producer fence for the buffer to be ready.
err = fenceWait(item.mFence->get(), fencePassThroughHandle);
if (err != OK) {
@@ -112,7 +120,6 @@
st.mCurrentFrameNumber = item.mFrameNumber;
st.computeCurrentTransformMatrixLocked();
- *outQueueEmpty = false;
*outDataspace = item.mDataSpace;
*outSlotid = slot;
return st.mSlots[slot].mGraphicBuffer;
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b78fc5d..138e08f 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -255,6 +255,7 @@
NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */
NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */
NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
+ NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC = 48, /* private */
// clang-format on
};
@@ -1022,6 +1023,12 @@
(int)compatibility);
}
+static inline int native_window_set_frame_timeline_vsync(struct ANativeWindow* window,
+ int64_t frameTimelineVsyncId) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC,
+ frameTimelineVsyncId);
+}
+
// ------------------------------------------------------------------------------------------------
// Candidates for APEX visibility
// These functions are planned to be made stable for APEX modules, but have not
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3dcb498..eb967ce 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -31,6 +31,7 @@
"libui",
"libutils",
],
+ whole_static_libs: ["libskia"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -71,13 +72,18 @@
],
}
+filegroup {
+ name: "librenderengine_skia_sources",
+ srcs: [
+ "skia/SkiaRenderEngine.cpp",
+ "skia/SkiaGLRenderEngine.cpp",
+ "skia/filters/BlurFilter.cpp",
+ ],
+}
+
cc_library_static {
name: "librenderengine",
defaults: ["librenderengine_defaults"],
- vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
clang: true,
cflags: [
@@ -88,6 +94,7 @@
":librenderengine_sources",
":librenderengine_gl_sources",
":librenderengine_threaded_sources",
+ ":librenderengine_skia_sources",
],
lto: {
thin: true,
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index c3fbb60..3e65d9a 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -18,10 +18,11 @@
#include <cutils/properties.h>
#include <log/log.h>
-#include <private/gui/SyncFeatures.h>
#include "gl/GLESRenderEngine.h"
#include "threaded/RenderEngineThreaded.h"
+#include "skia/SkiaGLRenderEngine.h"
+
namespace android {
namespace renderengine {
@@ -37,12 +38,17 @@
if (strcmp(prop, "threaded") == 0) {
renderEngineType = RenderEngineType::THREADED;
}
+ if (strcmp(prop, "skiagl") == 0) {
+ renderEngineType = RenderEngineType::SKIA_GL;
+ }
switch (renderEngineType) {
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
return renderengine::threaded::RenderEngineThreaded::create(
[args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+ case RenderEngineType::SKIA_GL:
+ return renderengine::skia::SkiaGLRenderEngine::create(args);
case RenderEngineType::GLES:
default:
ALOGD("RenderEngine with GLES Backend");
@@ -52,20 +58,5 @@
RenderEngine::~RenderEngine() = default;
-namespace impl {
-
-RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {}
-
-RenderEngine::~RenderEngine() = default;
-
-bool RenderEngine::useNativeFenceSync() const {
- return SyncFeatures::getInstance().useNativeFenceSync();
-}
-
-bool RenderEngine::useWaitSync() const {
- return SyncFeatures::getInstance().useWaitSync();
-}
-
-} // namespace impl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 672ffae..6adcbea 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -114,6 +114,28 @@
namespace renderengine {
namespace gl {
+class BindNativeBufferAsFramebuffer {
+public:
+ BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer,
+ const bool useFramebufferCache)
+ : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
+ useFramebufferCache)
+ ? mEngine.bindFrameBuffer(mFramebuffer)
+ : NO_MEMORY;
+ }
+ ~BindNativeBufferAsFramebuffer() {
+ mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
+ mEngine.unbindFrameBuffer(mFramebuffer);
+ }
+ status_t getStatus() const { return mStatus; }
+
+private:
+ GLESRenderEngine& mEngine;
+ Framebuffer* mFramebuffer;
+ status_t mStatus;
+};
+
using base::StringAppendF;
using ui::Dataspace;
@@ -334,8 +356,7 @@
GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLConfig config, EGLContext ctxt, EGLSurface stub,
EGLContext protectedContext, EGLSurface protectedStub)
- : renderengine::impl::RenderEngine(args),
- mEGLDisplay(display),
+ : mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
mStubSurface(stub),
@@ -344,7 +365,8 @@
mVpWidth(0),
mVpHeight(0),
mFramebufferImageCacheSize(args.imageCacheSize),
- mUseColorManagement(args.useColorManagement) {
+ mUseColorManagement(args.useColorManagement),
+ mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
@@ -429,15 +451,10 @@
GLESRenderEngine::~GLESRenderEngine() {
// Destroy the image manager first.
mImageManager = nullptr;
+ cleanFramebufferCache();
std::lock_guard<std::mutex> lock(mRenderingMutex);
unbindFrameBuffer(mDrawingBuffer.get());
mDrawingBuffer = nullptr;
- while (!mFramebufferImageCache.empty()) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
mImageCache.clear();
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -458,8 +475,7 @@
void GLESRenderEngine::primeCache() const {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- mArgs.useColorManagement,
- mArgs.precacheToneMapperShaderOnly);
+ mUseColorManagement, mPrecacheToneMapperShaderOnly);
}
base::unique_fd GLESRenderEngine::flush() {
@@ -622,13 +638,8 @@
}
}
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
- const sp<GraphicBuffer>& buffer,
- const sp<Fence>& bufferFence) {
- if (buffer == nullptr) {
- return BAD_VALUE;
- }
-
+void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
ATRACE_CALL();
bool found = false;
@@ -644,7 +655,8 @@
if (!found) {
status_t cacheResult = mImageManager->cache(buffer);
if (cacheResult != NO_ERROR) {
- return cacheResult;
+ ALOGE("Error with caching buffer: %d", cacheResult);
+ return;
}
}
@@ -661,7 +673,7 @@
// We failed creating the image if we got here, so bail out.
ALOGE("Failed to create an EGLImage when rendering");
bindExternalTextureImage(texName, *createImage());
- return NO_INIT;
+ return;
}
bindExternalTextureImage(texName, *cachedImage->second);
@@ -674,22 +686,22 @@
base::unique_fd fenceFd(bufferFence->dup());
if (fenceFd == -1) {
ALOGE("error dup'ing fence fd: %d", errno);
- return -errno;
+ return;
}
if (!waitFence(std::move(fenceFd))) {
ALOGE("failed to wait on fence fd");
- return UNKNOWN_ERROR;
+ return;
}
} else {
status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
if (err != NO_ERROR) {
ALOGE("error waiting for fence: %d", err);
- return err;
+ return;
}
}
}
- return NO_ERROR;
+ return;
}
void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
@@ -943,6 +955,7 @@
// Bind the texture to placeholder so that backing image data can be freed.
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
// Release the cached fence here, so that we don't churn reallocations when
// we could no-op repeated calls of this method instead.
mLastDrawFence = nullptr;
@@ -950,6 +963,20 @@
return true;
}
+void GLESRenderEngine::cleanFramebufferCache() {
+ std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
+ // Bind the texture to placeholder so that backing image data can be freed.
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+ glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+
+ while (!mFramebufferImageCache.empty()) {
+ EGLImageKHR expired = mFramebufferImageCache.front().second;
+ mFramebufferImageCache.pop_front();
+ eglDestroyImageKHR(mEGLDisplay, expired);
+ DEBUG_EGL_IMAGE_TRACKER_DESTROY();
+ }
+}
+
void GLESRenderEngine::checkErrors() const {
checkErrors(nullptr);
}
@@ -1276,7 +1303,8 @@
if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
glEnable(GL_BLEND);
- glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+ GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 2c6eae2..1779994 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -48,7 +48,7 @@
class GLImage;
class BlurFilter;
-class GLESRenderEngine : public impl::RenderEngine {
+class GLESRenderEngine : public RenderEngine {
public:
static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
@@ -60,13 +60,8 @@
void primeCache() const override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
- void bindExternalTextureImage(uint32_t texName, const Image& image) override;
- status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
- status_t bindFrameBuffer(Framebuffer* framebuffer) override;
- void unbindFrameBuffer(Framebuffer* framebuffer) override;
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
@@ -102,13 +97,15 @@
std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
protected:
- Framebuffer* getFramebufferForDrawing() override;
+ Framebuffer* getFramebufferForDrawing();
void dump(std::string& result) override EXCLUDES(mRenderingMutex)
EXCLUDES(mFramebufferImageCacheMutex);
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
private:
+ friend class BindNativeBufferAsFramebuffer;
+
enum GlesVersion {
GLES_VERSION_1_0 = 0x10000,
GLES_VERSION_1_1 = 0x10001,
@@ -133,6 +130,12 @@
status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
EXCLUDES(mRenderingMutex);
void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+ status_t bindFrameBuffer(Framebuffer* framebuffer);
+ void unbindFrameBuffer(Framebuffer* framebuffer);
+ void bindExternalTextureImage(uint32_t texName, const Image& image);
+ void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+ void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
// A data space is considered HDR data space if it has BT2020 color space
// with PQ or HLG transfer function.
@@ -228,6 +231,10 @@
// supports sRGB, DisplayP3 color spaces.
const bool mUseColorManagement = false;
+ // Whether only shaders performing tone mapping from HDR to SDR will be generated on
+ // primeCache().
+ const bool mPrecacheToneMapperShaderOnly = false;
+
// Cache of GL images that we'll store per GraphicBuffer ID
std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index ca16d2c..a637796 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -47,8 +47,8 @@
// DataSpace::UNKNOWN otherwise.
ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
- // Additional color transform to apply in linear space after transforming
- // to the output dataspace.
+ // Additional color transform to apply after transforming to the output
+ // dataspace, in non-linear space.
mat4 colorTransform = mat4();
// Region that will be cleared to (0, 0, 0, 1) prior to rendering.
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 09a0f65..11b8e44 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -44,7 +44,6 @@
namespace renderengine {
-class BindNativeBufferAsFramebuffer;
class Image;
class Mesh;
class Texture;
@@ -74,6 +73,7 @@
enum class RenderEngineType {
GLES = 1,
THREADED = 2,
+ SKIA_GL = 3,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
@@ -89,16 +89,8 @@
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
- virtual bool useNativeFenceSync() const = 0;
- virtual bool useWaitSync() const = 0;
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
- virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
- // Legacy public method used by devices that don't support native fence
- // synchronization in their GPU driver, as this method provides implicit
- // synchronization for latching buffers.
- virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) = 0;
// Caches Image resources for this buffer, but does not bind the buffer to
// a particular texture.
// Note that work is deferred to an additional thread, i.e. this call
@@ -116,10 +108,6 @@
// a buffer should never occur before binding the buffer if the caller
// called {bind, cache}ExternalTextureBuffer before calling unbind.
virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0;
- // When binding a native buffer, it must be done before setViewportAndProjection
- // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
- virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
- virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
enum class CleanupMode {
CLEAN_OUTPUT_RESOURCES,
@@ -185,15 +173,9 @@
const std::vector<const LayerSettings*>& layers,
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
+ virtual void cleanFramebufferCache() = 0;
protected:
- // Gets a framebuffer to render to. This framebuffer may or may not be
- // cached depending on the implementation.
- //
- // Note that this method does not transfer ownership, so the caller most not
- // live longer than RenderEngine.
- virtual Framebuffer* getFramebufferForDrawing() = 0;
- friend class BindNativeBufferAsFramebuffer;
friend class threaded::RenderEngineThreaded;
};
@@ -280,44 +262,6 @@
RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
};
-class BindNativeBufferAsFramebuffer {
-public:
- BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
- const bool useFramebufferCache)
- : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
- mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
- useFramebufferCache)
- ? mEngine.bindFrameBuffer(mFramebuffer)
- : NO_MEMORY;
- }
- ~BindNativeBufferAsFramebuffer() {
- mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
- mEngine.unbindFrameBuffer(mFramebuffer);
- }
- status_t getStatus() const { return mStatus; }
-
-private:
- RenderEngine& mEngine;
- Framebuffer* mFramebuffer;
- status_t mStatus;
-};
-
-namespace impl {
-
-// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
-class RenderEngine : public renderengine::RenderEngine {
-public:
- virtual ~RenderEngine() = 0;
-
- bool useNativeFenceSync() const override;
- bool useWaitSync() const override;
-
-protected:
- RenderEngine(const RenderEngineCreationArgs& args);
- const RenderEngineCreationArgs mArgs;
-};
-
-} // namespace impl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index e03dd58..95ee925 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,21 +35,12 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
MOCK_CONST_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
- MOCK_CONST_METHOD0(useNativeFenceSync, bool());
- MOCK_CONST_METHOD0(useWaitSync, bool());
- MOCK_CONST_METHOD0(isCurrent, bool());
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
- MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&));
- MOCK_METHOD3(bindExternalTextureBuffer,
- status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
- MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*));
- MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*));
MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
@@ -61,6 +52,7 @@
status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*));
+ MOCK_METHOD0(cleanFramebufferCache, void());
};
} // namespace mock
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
new file mode 100644
index 0000000..69ad189
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -0,0 +1,760 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#include <cstdint>
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GrContextOptions.h>
+#include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
+#include <SkColorSpace.h>
+#include <SkImage.h>
+#include <SkImageFilters.h>
+#include <SkShadowUtils.h>
+#include <SkSurface.h>
+#include <gl/GrGLInterface.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+
+#include <cmath>
+
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
+bool checkGlError(const char* op, int lineNumber);
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+ EGLint wanted, EGLConfig* outConfig) {
+ EGLint numConfigs = -1, n = 0;
+ eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+ configs.resize(n);
+
+ if (!configs.empty()) {
+ if (attribute != EGL_NONE) {
+ for (EGLConfig config : configs) {
+ EGLint value = 0;
+ eglGetConfigAttrib(dpy, config, attribute, &value);
+ if (wanted == value) {
+ *outConfig = config;
+ return NO_ERROR;
+ }
+ }
+ } else {
+ // just pick the first one
+ *outConfig = configs[0];
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+ EGLConfig* config) {
+ // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+ // it is to be used with WIFI displays
+ status_t err;
+ EGLint wantedAttribute;
+ EGLint wantedAttributeValue;
+
+ std::vector<EGLint> attribs;
+ if (renderableType) {
+ const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
+ const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
+
+ // Default to 8 bits per channel.
+ const EGLint tmpAttribs[] = {
+ EGL_RENDERABLE_TYPE,
+ renderableType,
+ EGL_RECORDABLE_ANDROID,
+ EGL_TRUE,
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_FRAMEBUFFER_TARGET_ANDROID,
+ EGL_TRUE,
+ EGL_RED_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_GREEN_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_BLUE_SIZE,
+ is1010102 ? 10 : 8,
+ EGL_ALPHA_SIZE,
+ is1010102 ? 2 : 8,
+ EGL_NONE,
+ };
+ std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
+ std::back_inserter(attribs));
+ wantedAttribute = EGL_NONE;
+ wantedAttributeValue = EGL_NONE;
+ } else {
+ // if no renderable type specified, fallback to a simplified query
+ wantedAttribute = EGL_NATIVE_VISUAL_ID;
+ wantedAttributeValue = format;
+ }
+
+ err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
+ config);
+ if (err == NO_ERROR) {
+ EGLint caveat;
+ if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+ ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+ }
+
+ return err;
+}
+
+// Converts an android dataspace to a supported SkColorSpace
+// Supported dataspaces are
+// 1. sRGB
+// 2. Display P3
+// 3. BT2020 PQ
+// 4. BT2020 HLG
+// Unknown primaries are mapped to BT709, and unknown transfer functions
+// are mapped to sRGB.
+static sk_sp<SkColorSpace> toColorSpace(ui::Dataspace dataspace) {
+ skcms_Matrix3x3 gamut;
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ gamut = SkNamedGamut::kRec2020;
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ gamut = SkNamedGamut::kDisplayP3;
+ break;
+ default:
+ ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace);
+ gamut = SkNamedGamut::kSRGB;
+ break;
+ }
+
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_HLG:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ default:
+ ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace);
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ }
+}
+
+std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create(
+ const RenderEngineCreationArgs& args) {
+ // initialize EGL for the default display
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(display, nullptr, nullptr)) {
+ LOG_ALWAYS_FATAL("failed to initialize EGL");
+ }
+
+ const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+ if (!eglVersion) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+ }
+
+ const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+ if (!eglExtensions) {
+ checkGlError(__FUNCTION__, __LINE__);
+ LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+ }
+
+ auto& extensions = gl::GLExtensions::getInstance();
+ extensions.initWithEGLStrings(eglVersion, eglExtensions);
+
+ // The code assumes that ES2 or later is available if this extension is
+ // supported.
+ EGLConfig config = EGL_NO_CONFIG_KHR;
+ if (!extensions.hasNoConfigContext()) {
+ config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
+ }
+
+ bool useContextPriority =
+ extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+ EGLContext protectedContext = EGL_NO_CONTEXT;
+ if (args.enableProtectedContext && extensions.hasProtectedContent()) {
+ protectedContext = createEglContext(display, config, nullptr, useContextPriority,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
+ }
+
+ EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
+ Protection::UNPROTECTED);
+
+ // if can't create a GL context, we can only abort.
+ LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+ EGLSurface placeholder = EGL_NO_SURFACE;
+ if (!extensions.hasSurfacelessContext()) {
+ placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::UNPROTECTED);
+ LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer");
+ }
+ EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current");
+ extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+ glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+ EGLSurface protectedPlaceholder = EGL_NO_SURFACE;
+ if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
+ protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat,
+ Protection::PROTECTED);
+ ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE,
+ "can't create protected placeholder pbuffer");
+ }
+
+ // initialize the renderer while GL is current
+ std::unique_ptr<SkiaGLRenderEngine> engine =
+ std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
+ protectedContext, protectedPlaceholder);
+
+ ALOGI("OpenGL ES informations:");
+ ALOGI("vendor : %s", extensions.getVendor());
+ ALOGI("renderer : %s", extensions.getRenderer());
+ ALOGI("version : %s", extensions.getVersion());
+ ALOGI("extensions: %s", extensions.getExtensions());
+ ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+ ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+ return engine;
+}
+
+EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+ status_t err;
+ EGLConfig config;
+
+ // First try to get an ES3 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES3 fails, try to get an ES2 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES2 still doesn't work, probably because we're on the emulator.
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ err = selectEGLConfig(display, format, 0, &config);
+ if (err != NO_ERROR) {
+ // this EGL is too lame for android
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ }
+ }
+ }
+
+ if (logConfig) {
+ // print some debugging info
+ EGLint r, g, b, a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ ALOGI("EGL information:");
+ ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
+ ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
+ ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+ ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+ ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+ }
+
+ return config;
+}
+
+SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+ EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
+ EGLContext protectedContext, EGLSurface protectedPlaceholder)
+ : mEGLDisplay(display),
+ mEGLConfig(config),
+ mEGLContext(ctxt),
+ mPlaceholderSurface(placeholder),
+ mProtectedEGLContext(protectedContext),
+ mProtectedPlaceholderSurface(protectedPlaceholder),
+ mUseColorManagement(args.useColorManagement) {
+ // Suppress unused field warnings for things we definitely will need/use
+ // These EGL fields will all be needed for toggling between protected & unprotected contexts
+ // Or we need different RE instances for that
+ (void)mEGLDisplay;
+ (void)mEGLConfig;
+ (void)mEGLContext;
+ (void)mPlaceholderSurface;
+ (void)mProtectedEGLContext;
+ (void)mProtectedPlaceholderSurface;
+
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+ GrContextOptions options;
+ options.fPreferExternalImagesOverES3 = true;
+ options.fDisableDistanceFieldPaths = true;
+ mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);
+
+ if (args.supportsBackgroundBlur) {
+ mBlurFilter = new BlurFilter();
+ }
+}
+
+base::unique_fd SkiaGLRenderEngine::flush() {
+ ATRACE_CALL();
+ if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
+ }
+
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
+
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+ }
+
+ return fenceFd;
+}
+
+bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) {
+ if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
+ !gl::GLExtensions::getInstance().hasWaitSync()) {
+ return false;
+ }
+
+ // release the fd and transfer the ownership to EGLSync
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should return an
+ // EGLint or void. Ignore the return value for now, as it's not strictly
+ // needed.
+ eglWaitSyncKHR(mEGLDisplay, sync, 0);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (error != EGL_SUCCESS) {
+ ALOGE("failed to wait for EGL native fence sync: %#x", error);
+ return false;
+ }
+
+ return true;
+}
+
+static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) {
+ return !!(desc.usage & usage);
+}
+
+static float toDegrees(uint32_t transform) {
+ switch (transform) {
+ case ui::Transform::ROT_90:
+ return 90.0;
+ case ui::Transform::ROT_180:
+ return 180.0;
+ case ui::Transform::ROT_270:
+ return 270.0;
+ default:
+ return 0.0;
+ }
+}
+
+static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
+ return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
+ matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
+ matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
+ matrix[3][3], 0);
+}
+
+void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ mImageCache.erase(bufferId);
+}
+
+status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer,
+ const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+ ATRACE_NAME("SkiaGL::drawLayers");
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (layers.empty()) {
+ ALOGV("Drawing empty layer stack");
+ return NO_ERROR;
+ }
+
+ if (bufferFence.get() >= 0) {
+ // Duplicate the fence for passing to waitFence.
+ base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+ if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+ ATRACE_NAME("Waiting before draw");
+ sync_wait(bufferFence.get(), -1);
+ }
+ }
+ if (buffer == nullptr) {
+ ALOGE("No output buffer provided. Aborting GPU composition.");
+ return BAD_VALUE;
+ }
+
+ AHardwareBuffer_Desc bufferDesc;
+ AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
+
+ LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
+ "missing usage");
+
+ sk_sp<SkSurface> surface;
+ if (useFramebufferCache) {
+ auto iter = mSurfaceCache.find(buffer->getId());
+ if (iter != mSurfaceCache.end()) {
+ ALOGV("Cache hit!");
+ surface = iter->second;
+ }
+ }
+ if (!surface) {
+ surface = SkSurface::MakeFromAHardwareBuffer(mGrContext.get(), buffer->toAHardwareBuffer(),
+ GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
+ mUseColorManagement
+ ? toColorSpace(display.outputDataspace)
+ : SkColorSpace::MakeSRGB(),
+ nullptr);
+ if (useFramebufferCache && surface) {
+ ALOGD("Adding to cache");
+ mSurfaceCache.insert({buffer->getId(), surface});
+ }
+ }
+ if (!surface) {
+ ALOGE("Failed to make surface");
+ return BAD_VALUE;
+ }
+
+ auto canvas = surface->getCanvas();
+ // Clear the entire canvas with a transparent black to prevent ghost images.
+ canvas->clear(SK_ColorTRANSPARENT);
+ canvas->save();
+
+ // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+ // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+ // displays might have different scaling when compared to the physical screen.
+
+ canvas->clipRect(getSkRect(display.physicalDisplay));
+ canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
+
+ const auto clipWidth = display.clip.width();
+ const auto clipHeight = display.clip.height();
+ auto rotatedClipWidth = clipWidth;
+ auto rotatedClipHeight = clipHeight;
+ // Scale is contingent on the rotation result.
+ if (display.orientation & ui::Transform::ROT_90) {
+ std::swap(rotatedClipWidth, rotatedClipHeight);
+ }
+ const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+ static_cast<SkScalar>(rotatedClipWidth);
+ const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+ static_cast<SkScalar>(rotatedClipHeight);
+ canvas->scale(scaleX, scaleY);
+
+ // Canvas rotation is done by centering the clip window at the origin, rotating, translating
+ // back so that the top left corner of the clip is at (0, 0).
+ canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
+ canvas->rotate(toDegrees(display.orientation));
+ canvas->translate(-clipWidth / 2, -clipHeight / 2);
+ canvas->translate(-display.clip.left, -display.clip.top);
+ for (const auto& layer : layers) {
+ SkPaint paint;
+ const auto& bounds = layer->geometry.boundaries;
+ const auto dest = getSkRect(bounds);
+
+ if (layer->backgroundBlurRadius > 0) {
+ ATRACE_NAME("BackgroundBlur");
+ mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+ }
+
+ if (layer->source.buffer.buffer) {
+ ATRACE_NAME("DrawImage");
+ const auto& item = layer->source.buffer;
+ const auto bufferWidth = item.buffer->getBounds().width();
+ const auto bufferHeight = item.buffer->getBounds().height();
+ sk_sp<SkImage> image;
+ auto iter = mImageCache.find(item.buffer->getId());
+ if (iter != mImageCache.end()) {
+ image = iter->second;
+ } else {
+ image = SkImage::MakeFromAHardwareBuffer(item.buffer->toAHardwareBuffer(),
+ item.usePremultipliedAlpha
+ ? kPremul_SkAlphaType
+ : kUnpremul_SkAlphaType,
+ mUseColorManagement
+ ? toColorSpace(
+ layer->sourceDataspace)
+ : SkColorSpace::MakeSRGB());
+ mImageCache.insert({item.buffer->getId(), image});
+ }
+
+ SkMatrix matrix;
+ if (layer->geometry.roundedCornersRadius > 0) {
+ const auto roundedRect = getRoundedRect(layer);
+ matrix.setTranslate(roundedRect.getBounds().left() - dest.left(),
+ roundedRect.getBounds().top() - dest.top());
+ } else {
+ matrix.setIdentity();
+ }
+
+ auto texMatrix = getSkM44(item.textureTransform).asM33();
+ // textureTansform was intended to be passed directly into a shader, so when
+ // building the total matrix with the textureTransform we need to first
+ // normalize it, then apply the textureTransform, then scale back up.
+ matrix.postScale(1.0f / bufferWidth, 1.0f / bufferHeight);
+
+ auto rotatedBufferWidth = bufferWidth;
+ auto rotatedBufferHeight = bufferHeight;
+
+ // Swap the buffer width and height if we're rotating, so that we
+ // scale back up by the correct factors post-rotation.
+ if (texMatrix.getSkewX() <= -0.5f || texMatrix.getSkewX() >= 0.5f) {
+ std::swap(rotatedBufferWidth, rotatedBufferHeight);
+ // TODO: clean this up.
+ // GLESRenderEngine specifies its texture coordinates in
+ // CW orientation under OpenGL conventions, when they probably should have
+ // been CCW instead. The net result is that orientation
+ // transforms are applied in the reverse
+ // direction to render the correct result, because SurfaceFlinger uses the inverse
+ // of the display transform to correct for that. But this means that
+ // the tex transform passed by SkiaGLRenderEngine will rotate
+ // individual layers in the reverse orientation. Hack around it
+ // by injected a 180 degree rotation, but ultimately this is
+ // a bug in how SurfaceFlinger invokes the RenderEngine
+ // interface, so the proper fix should live there, and GLESRenderEngine
+ // should be fixed accordingly.
+ matrix.postRotate(180, 0.5, 0.5);
+ }
+
+ matrix.postConcat(texMatrix);
+ matrix.postScale(rotatedBufferWidth, rotatedBufferHeight);
+ paint.setShader(image->makeShader(matrix));
+ } else {
+ ATRACE_NAME("DrawColor");
+ const auto color = layer->source.solidColor;
+ paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
+ }
+
+ paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
+
+ // Layers have a local transform matrix that should be applied to them.
+ canvas->save();
+ canvas->concat(getSkM44(layer->geometry.positionTransform));
+
+ if (layer->shadow.length > 0) {
+ const auto rect = layer->geometry.roundedCornersRadius > 0
+ ? getSkRect(layer->geometry.roundedCornersCrop)
+ : dest;
+ drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+ }
+
+ if (layer->geometry.roundedCornersRadius > 0) {
+ canvas->drawRRect(getRoundedRect(layer), paint);
+ } else {
+ canvas->drawRect(dest, paint);
+ }
+ canvas->restore();
+ }
+ {
+ ATRACE_NAME("flush surface");
+ surface->flush();
+ }
+ canvas->restore();
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+ if (requireSync) {
+ ATRACE_BEGIN("Submit(sync=true)");
+ } else {
+ ATRACE_BEGIN("Submit(sync=false)");
+ }
+ bool success = mGrContext->submit(requireSync);
+ ATRACE_END();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+
+ // checkErrors();
+ return NO_ERROR;
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {
+ return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
+ const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+ const auto cornerRadius = layer->geometry.roundedCornersRadius;
+ return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
+}
+
+inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
+ return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
+}
+
+inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
+ return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
+ matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
+ matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
+ matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
+}
+
+inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) {
+ return SkPoint3::Make(vector.x, vector.y, vector.z);
+}
+
+size_t SkiaGLRenderEngine::getMaxTextureSize() const {
+ return mGrContext->maxTextureSize();
+}
+
+size_t SkiaGLRenderEngine::getMaxViewportDims() const {
+ return mGrContext->maxRenderTargetSize();
+}
+
+void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const auto shadowShape = cornerRadius > 0
+ ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius))
+ : SkPath::Rect(casterRect);
+ const auto flags =
+ settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
+
+ SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ),
+ getSkPoint3(settings.lightPos), settings.lightRadius,
+ getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
+ flags);
+}
+
+EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority,
+ Protection protection) {
+ EGLint renderableType = 0;
+ if (config == EGL_NO_CONFIG_KHR) {
+ renderableType = EGL_OPENGL_ES3_BIT;
+ } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+ EGLint contextClientVersion = 0;
+ if (renderableType & EGL_OPENGL_ES3_BIT) {
+ contextClientVersion = 3;
+ } else if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(7);
+ contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ contextAttributes.push_back(contextClientVersion);
+ if (useContextPriority) {
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ }
+ if (protection == Protection::PROTECTED) {
+ contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ contextAttributes.push_back(EGL_TRUE);
+ }
+ contextAttributes.push_back(EGL_NONE);
+
+ EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+
+ if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
+ // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
+ // EGL_NO_CONTEXT so that we can abort.
+ if (config != EGL_NO_CONFIG_KHR) {
+ return context;
+ }
+ // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we
+ // should try to fall back to GLES 2.
+ contextAttributes[1] = 2;
+ context = eglCreateContext(display, config, shareContext, contextAttributes.data());
+ }
+
+ return context;
+}
+
+EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
+ EGLConfig config, int hwcFormat,
+ Protection protection) {
+ EGLConfig placeholderConfig = config;
+ if (placeholderConfig == EGL_NO_CONFIG_KHR) {
+ placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+ std::vector<EGLint> attributes;
+ attributes.reserve(7);
+ attributes.push_back(EGL_WIDTH);
+ attributes.push_back(1);
+ attributes.push_back(EGL_HEIGHT);
+ attributes.push_back(1);
+ if (protection == Protection::PROTECTED) {
+ attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
+ attributes.push_back(EGL_TRUE);
+ }
+ attributes.push_back(EGL_NONE);
+
+ return eglCreatePbufferSurface(display, placeholderConfig, attributes.data());
+}
+
+void SkiaGLRenderEngine::cleanFramebufferCache() {
+ mSurfaceCache.clear();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
new file mode 100644
index 0000000..ed4ba11
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_SKIAGLRENDERENGINE_H_
+#define SF_SKIAGLRENDERENGINE_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GrDirectContext.h>
+#include <SkSurface.h>
+#include <android-base/thread_annotations.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "EGL/egl.h"
+#include "SkiaRenderEngine.h"
+#include "filters/BlurFilter.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
+public:
+ static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
+ SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+ EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
+ EGLSurface protectedPlaceholder);
+ ~SkiaGLRenderEngine() override{};
+
+ void unbindExternalTextureBuffer(uint64_t bufferId) override;
+ status_t drawLayers(const DisplaySettings& display,
+ const std::vector<const LayerSettings*>& layers,
+ const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+ void cleanFramebufferCache() override;
+
+protected:
+ void dump(std::string& /*result*/) override{};
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+private:
+ static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+ static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority,
+ Protection protection);
+ static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat, Protection protection);
+ inline SkRect getSkRect(const FloatRect& layer);
+ inline SkRect getSkRect(const Rect& layer);
+ inline SkRRect getRoundedRect(const LayerSettings* layer);
+ inline SkColor getSkColor(const vec4& color);
+ inline SkM44 getSkM44(const mat4& matrix);
+ inline SkPoint3 getSkPoint3(const vec3& vector);
+
+ base::unique_fd flush();
+ bool waitFence(base::unique_fd fenceFd);
+ void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
+
+ EGLDisplay mEGLDisplay;
+ EGLConfig mEGLConfig;
+ EGLContext mEGLContext;
+ EGLSurface mPlaceholderSurface;
+ EGLContext mProtectedEGLContext;
+ EGLSurface mProtectedPlaceholderSurface;
+ BlurFilter* mBlurFilter = nullptr;
+
+ const bool mUseColorManagement;
+
+ // Cache of GL images that we'll store per GraphicBuffer ID
+ std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex);
+ // Mutex guarding rendering operations, so that:
+ // 1. GL operations aren't interleaved, and
+ // 2. Internal state related to rendering that is potentially modified by
+ // multiple threads is guaranteed thread-safe.
+ std::mutex mRenderingMutex;
+
+ sp<Fence> mLastDrawFence;
+
+ sk_sp<GrDirectContext> mGrContext;
+
+ std::unordered_map<uint64_t, sk_sp<SkSurface>> mSurfaceCache;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
diff --git a/libs/ui/UiConfig.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
similarity index 68%
rename from libs/ui/UiConfig.cpp
rename to libs/renderengine/skia/SkiaRenderEngine.cpp
index 0ac863d..81f0b6f 100644
--- a/libs/ui/UiConfig.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-#include <ui/UiConfig.h>
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
namespace android {
-
-void appendUiConfigString(std::string& configStr) {
- static const char* config =
- " [libui]";
- configStr.append(config);
-}
-
-
-}; // namespace android
+namespace renderengine {
+namespace skia {} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
new file mode 100644
index 0000000..2352c7e
--- /dev/null
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef SF_SKIARENDERENGINE_H_
+#define SF_SKIARENDERENGINE_H_
+
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+namespace android {
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace skia {
+
+class BlurFilter;
+
+// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends
+// Currently mostly just handles all the no-op / missing APIs
+class SkiaRenderEngine : public RenderEngine {
+public:
+ static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
+ ~SkiaRenderEngine() override {}
+
+ virtual void primeCache() const override{};
+ virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
+ virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
+ virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
+ virtual void unbindExternalTextureBuffer(uint64_t /*bufferId*/){};
+
+ virtual bool isProtected() const override { return false; } // mInProtectedContext; }
+ virtual bool supportsProtectedContent() const override { return false; };
+ virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; };
+ virtual status_t drawLayers(const DisplaySettings& /*display*/,
+ const std::vector<const LayerSettings*>& /*layers*/,
+ const sp<GraphicBuffer>& /*buffer*/,
+ const bool /*useFramebufferCache*/,
+ base::unique_fd&& /*bufferFence*/,
+ base::unique_fd* /*drawFence*/) override {
+ return 0;
+ };
+ virtual bool cleanupPostRender(CleanupMode) override { return true; };
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
new file mode 100644
index 0000000..eb791a7
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+BlurFilter::BlurFilter() {
+ SkString blurString(R"(
+ in shader input;
+ uniform float in_inverseScale;
+ uniform float2 in_blurOffset;
+
+ half4 main(float2 xy) {
+ float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
+
+ float4 c = float4(sample(input, scaled_xy));
+ c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, in_blurOffset.y)));
+ c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)));
+ c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y)));
+ c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)));
+
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::Make(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+}
+
+void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const {
+ ATRACE_CALL();
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ float tmpRadius = (float)blurRadius / 6.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale,
+ (float)input->height() * kInputScale);
+ auto drawSurface = canvas->makeSurface(scaledInfo);
+
+ const float stepX = radiusByPasses;
+ const float stepY = radiusByPasses;
+
+ // start by drawing and downscaling and doing the first blur pass
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("input") = input->makeImageSnapshot()->makeShader();
+ blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
+ blurBuilder.uniform("in_blurOffset") =
+ SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
+
+ {
+ // limit the lifetime of the input surface's snapshot to ensure that it goes out of
+ // scope before the surface is written into to avoid any copy-on-write behavior.
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr, false));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
+ blurBuilder.child("input") = nullptr;
+ }
+
+ // And now we'll ping pong between our surfaces, to accumulate the result of various offsets.
+ auto lastDrawTarget = drawSurface;
+ if (numberOfPasses > 1) {
+ auto readSurface = drawSurface;
+ drawSurface = canvas->makeSurface(scaledInfo);
+
+ for (auto i = 1; i < numberOfPasses; i++) {
+ const float stepScale = (float)i * kInputScale;
+
+ blurBuilder.child("input") = readSurface->makeImageSnapshot()->makeShader();
+ blurBuilder.uniform("in_inverseScale") = 1.0f;
+ blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
+
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr, false));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
+
+ // Swap buffers for next iteration
+ auto tmp = drawSurface;
+ drawSurface = readSurface;
+ readSurface = tmp;
+ blurBuilder.child("input") = nullptr;
+ }
+ lastDrawTarget = readSurface;
+ }
+
+ drawSurface->flushAndSubmit();
+
+ // do the final composition, with alpha blending to hide downscaling artifacts.
+ {
+ SkPaint paint;
+ paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader(
+ SkMatrix::MakeScale(kInverseInputScale)));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
+ canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
+ }
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
new file mode 100644
index 0000000..94b3673
--- /dev/null
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -0,0 +1,59 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class BlurFilter {
+public:
+ // Downsample FBO to improve performance
+ static constexpr float kInputScale = 0.25f;
+ // Downsample scale factor used to improve performance
+ static constexpr float kInverseInputScale = 1.0f / kInputScale;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+ explicit BlurFilter();
+ virtual ~BlurFilter(){};
+
+ // Execute blur passes, rendering to a canvas.
+ void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index ba17143..d795616 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -919,7 +919,7 @@
void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
fillRedBufferWithoutPremultiplyAlpha();
- expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
}
void RenderEngineTest::clearLeftRegion() {
@@ -1242,31 +1242,6 @@
EXPECT_EQ(NO_ERROR, barrier->result);
}
-TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) {
- status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
- ASSERT_EQ(BAD_VALUE, result);
-}
-
-TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) {
- sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
- uint32_t texName;
- sRE->genTextures(1, &texName);
- mTexNames.push_back(texName);
-
- sRE->bindExternalTextureBuffer(texName, buf, nullptr);
- uint64_t bufferId = buf->getId();
- EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
- std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
- sRE->unbindExternalTextureBufferForTesting(bufferId);
- std::lock_guard<std::mutex> lock(barrier->mutex);
- ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5),
- [&]() REQUIRES(barrier->mutex) {
- return barrier->isOpen;
- }));
- EXPECT_EQ(NO_ERROR, barrier->result);
- EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
-}
-
TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
sRE->cacheExternalTextureBufferForTesting(nullptr);
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 97c7442..ba5175d 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -62,21 +62,6 @@
mThreadedRE->deleteTextures(1, &texName);
}
-TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) {
- EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr)))
- .WillOnce(Return(BAD_VALUE));
- status_t result = mThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr);
- ASSERT_EQ(BAD_VALUE, result);
-}
-
-TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
- sp<GraphicBuffer> buf = new GraphicBuffer();
- EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
- .WillOnce(Return(NO_ERROR));
- status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
- ASSERT_EQ(NO_ERROR, result);
-}
-
TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
mThreadedRE->cacheExternalTextureBuffer(nullptr);
@@ -93,26 +78,6 @@
mThreadedRE->unbindExternalTextureBuffer(0x0);
}
-TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) {
- std::unique_ptr<renderengine::Framebuffer> framebuffer;
- EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE));
- status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
- ASSERT_EQ(BAD_VALUE, result);
-}
-
-TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) {
- std::unique_ptr<renderengine::Framebuffer> framebuffer;
- EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR));
- status_t result = mThreadedRE->bindFrameBuffer(framebuffer.get());
- ASSERT_EQ(NO_ERROR, result);
-}
-
-TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) {
- std::unique_ptr<renderengine::Framebuffer> framebuffer;
- EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get()));
- mThreadedRE->unbindFrameBuffer(framebuffer.get());
-}
-
TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
size_t size = 20;
EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index d4184fd..5453302 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -116,36 +116,6 @@
result.assign(resultFuture.get());
}
-bool RenderEngineThreaded::useNativeFenceSync() const {
- std::promise<bool> resultPromise;
- std::future<bool> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
- ATRACE_NAME("REThreaded::useNativeFenceSync");
- bool returnValue = SyncFeatures::getInstance().useNativeFenceSync();
- resultPromise.set_value(returnValue);
- });
- }
- mCondition.notify_one();
- return resultFuture.get();
-}
-
-bool RenderEngineThreaded::useWaitSync() const {
- std::promise<bool> resultPromise;
- std::future<bool> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& /*instance*/) {
- ATRACE_NAME("REThreaded::useWaitSync");
- bool returnValue = SyncFeatures::getInstance().useWaitSync();
- resultPromise.set_value(returnValue);
- });
- }
- mCondition.notify_one();
- return resultFuture.get();
-}
-
void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
@@ -176,40 +146,6 @@
resultFuture.wait();
}
-void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) {
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push(
- [&resultPromise, texName, &image](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::bindExternalTextureImage");
- instance.bindExternalTextureImage(texName, image);
- resultPromise.set_value();
- });
- }
- mCondition.notify_one();
- resultFuture.wait();
-}
-
-status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName,
- const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) {
- std::promise<status_t> resultPromise;
- std::future<status_t> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push(
- [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::bindExternalTextureBuffer");
- status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence);
- resultPromise.set_value(status);
- });
- }
- mCondition.notify_one();
- return resultFuture.get();
-}
-
void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
@@ -240,36 +176,6 @@
resultFuture.wait();
}
-status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) {
- std::promise<status_t> resultPromise;
- std::future<status_t> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::bindFrameBuffer");
- status_t status = instance.bindFrameBuffer(framebuffer);
- resultPromise.set_value(status);
- });
- }
- mCondition.notify_one();
- return resultFuture.get();
-}
-
-void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) {
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::unbindFrameBuffer");
- instance.unbindFrameBuffer(framebuffer);
- resultPromise.set_value();
- });
- }
- mCondition.notify_one();
- resultFuture.wait();
-}
-
size_t RenderEngineThreaded::getMaxTextureSize() const {
std::promise<size_t> resultPromise;
std::future<size_t> resultFuture = resultPromise.get_future();
@@ -346,21 +252,6 @@
return resultFuture.get();
}
-Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() {
- std::promise<Framebuffer*> resultPromise;
- std::future<Framebuffer*> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::getFramebufferForDrawing");
- Framebuffer* framebuffer = instance.getFramebufferForDrawing();
- resultPromise.set_value(framebuffer);
- });
- }
- mCondition.notify_one();
- return resultFuture.get();
-}
-
bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) {
std::promise<bool> resultPromise;
std::future<bool> resultFuture = resultPromise.get_future();
@@ -398,6 +289,21 @@
return resultFuture.get();
}
+void RenderEngineThreaded::cleanFramebufferCache() {
+ std::promise<void> resultPromise;
+ std::future<void> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::cleanFramebufferCache");
+ instance.cleanFramebufferCache();
+ resultPromise.set_value();
+ });
+ }
+ mCondition.notify_one();
+ resultFuture.wait();
+}
+
} // namespace threaded
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 86a49e9..cdfbd04 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -45,17 +45,10 @@
void dump(std::string& result) override;
- bool useNativeFenceSync() const override;
- bool useWaitSync() const override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
- void bindExternalTextureImage(uint32_t texName, const Image& image) override;
- status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) override;
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
void unbindExternalTextureBuffer(uint64_t bufferId) override;
- status_t bindFrameBuffer(Framebuffer* framebuffer) override;
- void unbindFrameBuffer(Framebuffer* framebuffer) override;
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
@@ -69,8 +62,7 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
-protected:
- Framebuffer* getFramebufferForDrawing() override;
+ void cleanFramebufferCache() override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 2acc5bb..3fa2e53 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -120,7 +120,6 @@
"PixelFormat.cpp",
"PublicFormat.cpp",
"Size.cpp",
- "UiConfig.cpp",
],
include_dirs: [
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 943d13e..91d2d58 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -83,20 +83,17 @@
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
- const size_t c = list.size();
- for (size_t i=0 ; i<c ; i++) {
+ const size_t count = list.size();
+ StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ "W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
+ for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
- if (rec.size) {
- StringAppendF(&result,
- "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
- list.keyAt(i), static_cast<double>(rec.size) / 1024.0, rec.width, rec.stride, rec.height,
- rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
- } else {
- StringAppendF(&result,
- "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n",
- list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount,
- rec.format, rec.usage, rec.requestorName.c_str());
- }
+ std::string sizeStr = (rec.size)
+ ? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
+ : "unknown";
+ StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
+ rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
}
StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
index 70e3ce7..a6595cf 100644
--- a/libs/ui/PublicFormat.cpp
+++ b/libs/ui/PublicFormat.cpp
@@ -35,6 +35,8 @@
case PublicFormat::RAW_SENSOR:
case PublicFormat::RAW_DEPTH:
return HAL_PIXEL_FORMAT_RAW16;
+ case PublicFormat::RAW_DEPTH10:
+ return HAL_PIXEL_FORMAT_RAW10;
default:
// Most formats map 1:1
return static_cast<int>(f);
@@ -50,6 +52,7 @@
case PublicFormat::DEPTH_POINT_CLOUD:
case PublicFormat::DEPTH16:
case PublicFormat::RAW_DEPTH:
+ case PublicFormat::RAW_DEPTH10:
dataspace = Dataspace::DEPTH;
break;
case PublicFormat::RAW_SENSOR:
@@ -80,6 +83,13 @@
PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) {
Dataspace ds = static_cast<Dataspace>(dataSpace);
switch (format) {
+ case HAL_PIXEL_FORMAT_RAW10:
+ switch (ds) {
+ case Dataspace::DEPTH:
+ return PublicFormat::RAW_DEPTH10;
+ default:
+ return PublicFormat::RAW10;
+ }
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_RGBA_FP16:
@@ -87,7 +97,6 @@
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_Y8:
- case HAL_PIXEL_FORMAT_RAW10:
case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_YCbCr_420_888:
case HAL_PIXEL_FORMAT_YV12:
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 6b1bb40..cd68c1c 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "Transform"
+
#include <math.h>
#include <android-base/stringprintf.h>
@@ -22,8 +25,7 @@
#include <ui/Transform.h>
#include <utils/String8.h>
-namespace android {
-namespace ui {
+namespace android::ui {
Transform::Transform() {
reset();
@@ -57,8 +59,7 @@
mMatrix[2][2] == other.mMatrix[2][2];
}
-Transform Transform::operator * (const Transform& rhs) const
-{
+Transform Transform::operator*(const Transform& rhs) const {
if (CC_LIKELY(mType == IDENTITY))
return rhs;
@@ -134,7 +135,7 @@
}
float Transform::getScaleX() const {
- return sqrt(dsdx() * dsdx()) + (dtdx() * dtdx());
+ return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx()));
}
float Transform::getScaleY() const {
@@ -150,8 +151,7 @@
}
}
-void Transform::set(float tx, float ty)
-{
+void Transform::set(float tx, float ty) {
mMatrix[2][0] = tx;
mMatrix[2][1] = ty;
mMatrix[2][2] = 1.0f;
@@ -163,8 +163,7 @@
}
}
-void Transform::set(float a, float b, float c, float d)
-{
+void Transform::set(float a, float b, float c, float d) {
mat33& M(mMatrix);
M[0][0] = a; M[1][0] = b;
M[0][1] = c; M[1][1] = d;
@@ -172,8 +171,7 @@
mType = UNKNOWN_TYPE;
}
-status_t Transform::set(uint32_t flags, float w, float h)
-{
+status_t Transform::set(uint32_t flags, float w, float h) {
if (flags & ROT_INVALID) {
// that's not allowed!
reset();
@@ -245,13 +243,11 @@
return transform(vec2(x, y));
}
-Rect Transform::makeBounds(int w, int h) const
-{
+Rect Transform::makeBounds(int w, int h) const {
return transform( Rect(w, h) );
}
-Rect Transform::transform(const Rect& bounds, bool roundOutwards) const
-{
+Rect Transform::transform(const Rect& bounds, bool roundOutwards) const {
Rect r;
vec2 lt( bounds.left, bounds.top );
vec2 rt( bounds.right, bounds.top );
@@ -278,8 +274,7 @@
return r;
}
-FloatRect Transform::transform(const FloatRect& bounds) const
-{
+FloatRect Transform::transform(const FloatRect& bounds) const {
vec2 lt(bounds.left, bounds.top);
vec2 rt(bounds.right, bounds.top);
vec2 lb(bounds.left, bounds.bottom);
@@ -299,8 +294,7 @@
return r;
}
-Region Transform::transform(const Region& reg) const
-{
+Region Transform::transform(const Region& reg) const {
Region out;
if (CC_UNLIKELY(type() > TRANSLATE)) {
if (CC_LIKELY(preserveRects())) {
@@ -320,8 +314,7 @@
return out;
}
-uint32_t Transform::type() const
-{
+uint32_t Transform::type() const {
if (mType & UNKNOWN_TYPE) {
// recompute what this transform is
@@ -416,16 +409,18 @@
return type() & 0xFF;
}
-uint32_t Transform::getOrientation() const
-{
+uint32_t Transform::getOrientation() const {
return (type() >> 8) & 0xFF;
}
-bool Transform::preserveRects() const
-{
+bool Transform::preserveRects() const {
return (getOrientation() & ROT_INVALID) ? false : true;
}
+bool Transform::needsBilinearFiltering() const {
+ return (!preserveRects() || getType() >= ui::Transform::SCALE);
+}
+
mat4 Transform::asMatrix4() const {
// Internally Transform uses a 3x3 matrix since the transform is meant for
// two-dimensional values. An equivalent 4x4 matrix means inserting an extra
@@ -515,6 +510,10 @@
}
out += "(" + transformToString(type) + ")\n";
+ if (type == IDENTITY) {
+ return;
+ }
+
for (size_t i = 0; i < 3; i++) {
StringAppendF(&out, "%s %.4f %.4f %.4f\n", prefix, static_cast<double>(mMatrix[0][i]),
static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
@@ -527,5 +526,4 @@
ALOGD("%s", out.c_str());
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 9eb5483..f196ab9 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -17,13 +17,20 @@
#pragma once
#include <cstdint>
-#include <functional>
+#include <optional>
#include <string>
namespace android {
// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
+// The encoding of the ID is type-specific for bits 0 to 61.
struct DisplayId {
+ // Flag indicating that the display is virtual.
+ static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63;
+
+ // Flag indicating that the ID is stable across reboots.
+ static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
// TODO(b/162612135) Remove default constructor
DisplayId() = default;
constexpr DisplayId(const DisplayId&) = default;
@@ -51,8 +58,12 @@
// DisplayId of a physical display, such as the internal display or externally connected display.
struct PhysicalDisplayId : DisplayId {
- // Flag indicating that the ID is stable across reboots.
- static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+ static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) {
+ if (id.value & FLAG_VIRTUAL) {
+ return std::nullopt;
+ }
+ return {PhysicalDisplayId(id)};
+ }
// Returns a stable ID based on EDID information.
static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
@@ -69,8 +80,8 @@
// TODO(b/162612135) Remove default constructor
PhysicalDisplayId() = default;
+ // TODO(b/162612135) Remove constructor
explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
- explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other.value) {}
constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
@@ -81,10 +92,82 @@
uint32_t modelHash)
: DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
(static_cast<uint64_t>(modelHash) << 8) | port) {}
+
+ explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
};
static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
+struct VirtualDisplayId : DisplayId {
+ using BaseId = uint32_t;
+ // Flag indicating that this virtual display is backed by the GPU.
+ static constexpr uint64_t FLAG_GPU = 1ULL << 61;
+
+ static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
+ if (id.value & FLAG_VIRTUAL) {
+ return {VirtualDisplayId(id)};
+ }
+ return std::nullopt;
+ }
+
+protected:
+ constexpr VirtualDisplayId(uint64_t flags, BaseId baseId)
+ : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {}
+
+ explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+struct HalVirtualDisplayId : VirtualDisplayId {
+ explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {}
+
+ static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
+ if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) {
+ return {HalVirtualDisplayId(id)};
+ }
+ return std::nullopt;
+ }
+
+private:
+ explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+struct GpuVirtualDisplayId : VirtualDisplayId {
+ explicit constexpr GpuVirtualDisplayId(BaseId baseId)
+ : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {}
+
+ static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
+ if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) {
+ return {GpuVirtualDisplayId(id)};
+ }
+ return std::nullopt;
+ }
+
+private:
+ explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+// HalDisplayId is the ID of a display which is managed by HWC.
+// PhysicalDisplayId and HalVirtualDisplayId are implicitly convertible to HalDisplayId.
+struct HalDisplayId : DisplayId {
+ constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {}
+ constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {}
+
+ static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) {
+ if (GpuVirtualDisplayId::tryCast(id)) {
+ return std::nullopt;
+ }
+ return {HalDisplayId(id)};
+ }
+
+private:
+ explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
+
} // namespace android
namespace std {
@@ -99,4 +182,13 @@
template <>
struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {};
+template <>
+struct hash<android::HalVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::GpuVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::HalDisplayId> : hash<android::DisplayId> {};
+
} // namespace std
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index 1152cc5..22274a2 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -50,6 +50,7 @@
JPEG = 0x100,
DEPTH_POINT_CLOUD = 0x101,
RAW_DEPTH = 0x1002, // @hide
+ RAW_DEPTH10 = 0x1003, // @hide
YV12 = 0x32315659,
Y8 = 0x20203859,
Y16 = 0x20363159, // @hide
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 6670dc0..58323e5 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -202,6 +202,15 @@
// the input.
Rect transform(uint32_t xform, int32_t width, int32_t height) const;
+ Rect scale(float scaleX, float scaleY) const {
+ return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY));
+ }
+
+ Rect& scaleSelf(float scaleX, float scaleY) {
+ set(scale(scaleX, scaleY));
+ return *this;
+ }
+
// this calculates (Region(*this) - exclude).bounds() efficiently
Rect reduce(const Rect& exclude) const;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 4c463bf..a197b3b 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -61,9 +61,13 @@
};
// query the transform
- bool preserveRects() const;
- uint32_t getType() const;
- uint32_t getOrientation() const;
+ bool preserveRects() const;
+
+ // Returns if bilinear filtering is needed after applying this transform to avoid aliasing.
+ bool needsBilinearFiltering() const;
+
+ uint32_t getType() const;
+ uint32_t getOrientation() const;
bool operator==(const Transform& other) const;
const vec3& operator [] (size_t i) const; // returns column i
@@ -107,7 +111,7 @@
void dump(std::string& result, const char* name, const char* prefix = "") const;
void dump(const char* name, const char* prefix = "") const;
- static RotationFlags toRotationFlags(Rotation);
+ static constexpr RotationFlags toRotationFlags(Rotation);
private:
struct mat33 {
@@ -132,7 +136,7 @@
*os << out;
}
-inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
+inline constexpr Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
switch (rotation) {
case ROTATION_0:
return ROT_0;
diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h
deleted file mode 100644
index d1d6014..0000000
--- a/libs/ui/include/ui/UiConfig.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_UI_CONFIG_H
-#define ANDROID_UI_CONFIG_H
-
-#include <string>
-
-namespace android {
-
-// Append the libui configuration details to configStr.
-void appendUiConfigString(std::string& configStr);
-
-}; // namespace android
-
-#endif /*ANDROID_UI_CONFIG_H*/
diff --git a/libs/ui/include_vndk/ui/UiConfig.h b/libs/ui/include_vndk/ui/UiConfig.h
deleted file mode 120000
index f580ce1..0000000
--- a/libs/ui/include_vndk/ui/UiConfig.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/UiConfig.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 28ef77a..d005ce8 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
}
cc_test {
+ name: "DisplayId_test",
+ shared_libs: ["libui"],
+ srcs: ["DisplayId_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "FlattenableHelpers_test",
shared_libs: ["libui"],
srcs: ["FlattenableHelpers_test.cpp"],
@@ -85,6 +92,14 @@
}
cc_test {
+ name: "Rect_test",
+ test_suites: ["device-tests"],
+ shared_libs: ["libui"],
+ srcs: ["Rect_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "Size_test",
test_suites: ["device-tests"],
shared_libs: ["libui"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
new file mode 100644
index 0000000..1d908b8
--- /dev/null
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 <ui/DisplayId.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(DisplayIdTest, createPhysicalIdFromEdid) {
+ constexpr uint8_t port = 1;
+ constexpr uint16_t manufacturerId = 13;
+ constexpr uint32_t modelHash = 42;
+ PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
+ EXPECT_EQ(port, id.getPort());
+ EXPECT_EQ(manufacturerId, id.getManufacturerId());
+ EXPECT_FALSE(VirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createPhysicalIdFromPort) {
+ constexpr uint8_t port = 3;
+ PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
+ EXPECT_EQ(port, id.getPort());
+ EXPECT_FALSE(VirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createGpuVirtualId) {
+ GpuVirtualDisplayId id(42);
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_FALSE(HalDisplayId::tryCast(id));
+}
+
+TEST(DisplayIdTest, createHalVirtualId) {
+ HalVirtualDisplayId id(42);
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
new file mode 100644
index 0000000..5499a5b
--- /dev/null
+++ b/libs/ui/tests/Rect_test.cpp
@@ -0,0 +1,262 @@
+/*
+ * 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 <system/graphics.h>
+#include <ui/FloatRect.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(RectTest, constructDefault) {
+ const Rect rect;
+ EXPECT_FALSE(rect.isValid());
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, constructFromWidthAndHeight) {
+ const Rect rect(100, 200);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromSize) {
+ const Rect rect(Size(100, 200));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(0, rect.top);
+ EXPECT_EQ(0, rect.left);
+ EXPECT_EQ(100, rect.right);
+ EXPECT_EQ(200, rect.bottom);
+ EXPECT_EQ(100, rect.getWidth());
+ EXPECT_EQ(200, rect.getHeight());
+}
+
+TEST(RectTest, constructFromLTRB) {
+ const Rect rect(11, 12, 14, 14);
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromPoints) {
+ const Rect rect(Point(11, 12), Point(14, 14));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(11, rect.left);
+ EXPECT_EQ(12, rect.top);
+ EXPECT_EQ(14, rect.right);
+ EXPECT_EQ(14, rect.bottom);
+ EXPECT_EQ(3, rect.getWidth());
+ EXPECT_EQ(2, rect.getHeight());
+}
+
+TEST(RectTest, constructFromFloatRect) {
+ {
+ const Rect rect(FloatRect(10, 20, 30, 40));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+ // Construct with floating point error
+ {
+ constexpr float kError = 1e-3;
+ const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError));
+ EXPECT_TRUE(rect.isValid());
+ EXPECT_FALSE(rect.isEmpty());
+ EXPECT_EQ(10, rect.left);
+ EXPECT_EQ(20, rect.top);
+ EXPECT_EQ(30, rect.right);
+ EXPECT_EQ(40, rect.bottom);
+ }
+}
+
+TEST(RectTest, makeInvalid) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_TRUE(rect.isValid());
+ rect.makeInvalid();
+ EXPECT_FALSE(rect.isValid());
+}
+
+TEST(RectTest, clear) {
+ Rect rect(10, 20, 60, 60);
+ EXPECT_FALSE(rect.isEmpty());
+ rect.clear();
+ EXPECT_TRUE(rect.isEmpty());
+}
+
+TEST(RectTest, getSize) {
+ const Rect rect(10, 20, 60, 60);
+ EXPECT_EQ(Size(50, 40), rect.getSize());
+}
+
+TEST(RectTest, getBounds) {
+ const Rect rect(10, 20, 60, 60);
+ const Rect bounds = rect.getBounds();
+ EXPECT_EQ(0, bounds.left);
+ EXPECT_EQ(0, bounds.top);
+ EXPECT_EQ(50, bounds.right);
+ EXPECT_EQ(40, bounds.bottom);
+ EXPECT_EQ(rect.getSize(), bounds.getSize());
+}
+
+TEST(RectTest, getCornerPoints) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Point(10, 20), rect.leftTop());
+ EXPECT_EQ(Point(10, 60), rect.leftBottom());
+ EXPECT_EQ(Point(50, 20), rect.rightTop());
+ EXPECT_EQ(Point(50, 60), rect.rightBottom());
+}
+
+TEST(RectTest, operatorEquals) {
+ const Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(rect, rect);
+ EXPECT_NE(Rect(0, 20, 50, 60), rect);
+ EXPECT_NE(Rect(10, 0, 50, 60), rect);
+ EXPECT_NE(Rect(10, 20, 0, 60), rect);
+ EXPECT_NE(Rect(10, 20, 50, 0), rect);
+}
+
+TEST(RectTest, operatorsPlusMinus) {
+ Rect rect = Rect(10, 20, 50, 60) + Point(1, 2);
+ EXPECT_EQ(Rect(11, 22, 51, 62), rect);
+ rect -= Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+
+ rect = Rect(10, 20, 50, 60) - Point(1, 2);
+ EXPECT_EQ(Rect(9, 18, 49, 58), rect);
+ rect += Point(1, 2);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+}
+
+TEST(RectTest, scale) {
+ Rect rect(10, 20, 50, 60);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f));
+ rect.scaleSelf(2.f, 3.f);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+
+ rect = Rect(10, 20, 50, 60);
+ constexpr float kError = 1e-3;
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError));
+ rect.scaleSelf(2.f - kError, 3.f - kError);
+ EXPECT_EQ(Rect(20, 60, 100, 180), rect);
+}
+
+TEST(RectTest, inset) {
+ Rect rect(10, 20, 50, 60);
+ rect.inset(0, 0, 0, 0);
+ EXPECT_EQ(Rect(10, 20, 50, 60), rect);
+ rect.inset(1, 2, 3, 4);
+ EXPECT_EQ(Rect(11, 22, 47, 56), rect);
+}
+
+TEST(RectTest, intersect) {
+ const Rect rect(10, 20, 50, 60);
+ Rect intersection;
+
+ // Intersect with self is self
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(10, 20, 50, 60), intersection);
+
+ // Intersect with rect contained in us
+ const Rect insideRect(11, 21, 45, 55);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(insideRect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Intersect with rect we are contained in
+ intersection.makeInvalid();
+ EXPECT_TRUE(insideRect.intersect(rect, &intersection));
+ EXPECT_EQ(insideRect, intersection);
+
+ // Empty intersection
+ intersection.makeInvalid();
+ EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection));
+ EXPECT_TRUE(intersection.isEmpty());
+
+ // Partial intersection
+ const Rect other(30, 40, 70, 80);
+ intersection.makeInvalid();
+ EXPECT_TRUE(rect.intersect(other, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+
+ // Intersetion is commutative
+ intersection.makeInvalid();
+ EXPECT_TRUE(other.intersect(rect, &intersection));
+ EXPECT_EQ(Rect(30, 40, 50, 60), intersection);
+}
+
+TEST(RectTest, reduce) {
+ const Rect rect(10, 20, 50, 60);
+
+ // Reduce with self is empty
+ EXPECT_TRUE(rect.reduce(rect).isEmpty());
+
+ // Reduce with rect entirely inside is a noop
+ const Rect insideRect(11, 21, 45, 55);
+ EXPECT_EQ(rect, rect.reduce(insideRect));
+
+ // Reduce with rect entirely outside is empty
+ EXPECT_TRUE(insideRect.reduce(rect).isEmpty());
+
+ // Reduce with rect on the right
+ EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70)));
+
+ // Reduce with rect on the left
+ EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70)));
+
+ // Reduce with rect at the top
+ EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40)));
+
+ // Reduce with rect at the bottom
+ EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70)));
+}
+
+TEST(RectTest, transform) {
+ const int32_t width = 100, height = 200;
+ const Rect rect(1, 1, 2, 3);
+ EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height));
+ EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height));
+ EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height));
+ EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height));
+ EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height));
+}
+
+TEST(RectTest, toFloatRect) {
+ const Rect rect(10, 20, 50, 60);
+ const FloatRect floatRect = rect.toFloatRect();
+ EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
+}
+
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 38f37ad..5f75aea 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -33,8 +33,7 @@
#include <gtest/gtest.h>
-namespace android {
-namespace ui {
+namespace android::ui {
TEST(SizeTest, BasicConstructionAndEqualityComparison) {
Size s(123, 456);
@@ -215,5 +214,4 @@
ClampTest(uint32_t(0), int32_t(0));
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
index 7fcd7de..eece18e 100644
--- a/libs/ui/tests/TEST_MAPPING
+++ b/libs/ui/tests/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "Size_test"
+ },
+ {
+ "name": "Rect_test"
}
]
}
diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h
index 1f1bcb3..e753e0d 100644
--- a/opengl/include/EGL/eglext_angle.h
+++ b/opengl/include/EGL/eglext_angle.h
@@ -186,6 +186,26 @@
#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F
#endif /* EGL_ANGLE_create_context_extensions_enabled */
+#ifndef EGL_ANGLE_feature_control
+#define EGL_ANGLE_feature_control 1
+#define EGL_FEATURE_NAME_ANGLE 0x3460
+#define EGL_FEATURE_CATEGORY_ANGLE 0x3461
+#define EGL_FEATURE_DESCRIPTION_ANGLE 0x3462
+#define EGL_FEATURE_BUG_ANGLE 0x3463
+#define EGL_FEATURE_STATUS_ANGLE 0x3464
+#define EGL_FEATURE_COUNT_ANGLE 0x3465
+#define EGL_FEATURE_OVERRIDES_ENABLED_ANGLE 0x3466
+#define EGL_FEATURE_OVERRIDES_DISABLED_ANGLE 0x3467
+#define EGL_FEATURE_CONDITION_ANGLE 0x3468
+#define EGL_FEATURE_ALL_DISABLED_ANGLE 0x3469
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGIANGLEPROC) (EGLDisplay dpy, EGLint name, EGLint index);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBANGLEPROC) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI const char *EGLAPIENTRY eglQueryStringiANGLE(EGLDisplay dpy, EGLint name, EGLint index);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribANGLE(EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
+#endif
+#endif /* EGL_ANGLE_feature_control */
+
// clang-format on
#endif // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 07ec327..0b755aa 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -74,7 +74,8 @@
return eglDisplay ? eglDisplay->getRefsCount() : 0;
}
-egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
+std::map<EGLDisplay, std::unique_ptr<egl_display_t>> egl_display_t::displayMap;
+std::mutex egl_display_t::displayMapLock;
egl_display_t::egl_display_t()
: magic('_dpy'),
@@ -93,11 +94,12 @@
return nullptr;
}
- uintptr_t index = uintptr_t(dpy) - 1U;
- if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
+ const std::lock_guard<std::mutex> lock(displayMapLock);
+ auto search = displayMap.find(dpy);
+ if (search == displayMap.end() || !search->second->isValid()) {
return nullptr;
}
- return &sDisplay[index];
+ return search->second.get();
}
void egl_display_t::addObject(egl_object_t* object) {
@@ -125,7 +127,7 @@
const EGLAttrib* attrib_list) {
if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
- return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
+ return getPlatformDisplay(disp, attrib_list);
}
static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
@@ -141,6 +143,16 @@
attrs.push_back(attr[1]);
}
}
+ const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
+ std::vector<const char*> features;
+ if (eglFeatures.size() > 0) {
+ for (const std::string& eglFeature : eglFeatures) {
+ features.push_back(eglFeature.c_str());
+ }
+ features.push_back(0);
+ attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
+ attrs.push_back(reinterpret_cast<EGLAttrib>(features.data()));
+ }
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
@@ -170,7 +182,6 @@
EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
const EGLAttrib* attrib_list) {
- std::lock_guard<std::mutex> _l(lock);
ATRACE_CALL();
// get our driver loader
@@ -206,13 +217,20 @@
}
}
- disp.dpy = dpy;
if (dpy == EGL_NO_DISPLAY) {
loader.close(cnx);
+ } else {
+ const std::lock_guard<std::mutex> lock(displayMapLock);
+ if (displayMap.find(dpy) == displayMap.end()) {
+ auto d = std::make_unique<egl_display_t>();
+ d->disp.dpy = dpy;
+ displayMap[dpy] = std::move(d);
+ }
+ return dpy;
}
}
- return EGLDisplay(uintptr_t(display) + 1U);
+ return nullptr;
}
EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 0155133..87c2176 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -23,6 +23,8 @@
#include <stdint.h>
#include <condition_variable>
+#include <map>
+#include <memory>
#include <mutex>
#include <string>
#include <unordered_set>
@@ -40,9 +42,11 @@
bool needsAndroidPEglMitigation();
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
- static egl_display_t sDisplay[NUM_DISPLAYS];
+ static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap;
+ static std::mutex displayMapLock;
EGLDisplay getDisplay(EGLNativeDisplayType display);
- EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
+ static EGLDisplay getPlatformDisplay(EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list);
void loseCurrentImpl(egl_context_t* cur_c);
public:
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 1bcaab4..9a9bca1 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -44,11 +44,11 @@
defaults: ["libgpuservice_defaults"],
cflags: [
"-fvisibility=hidden",
- "-fwhole-program-vtables", // requires ThinLTO
],
lto: {
thin: true,
},
+ whole_program_vtables: true, // Requires ThinLTO
}
filegroup {
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index 45e8367..c5f8859 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -60,7 +60,7 @@
void SetUp() override {
SKIP_IF_BPF_NOT_SUPPORTED;
- ASSERT_EQ(0, bpf::setrlimitForTest());
+ bpf::setrlimitForTest();
mGpuMem = std::make_unique<GpuMem>();
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 6366e1d..000cf27 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -80,6 +80,7 @@
mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) {
GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
packet->set_timestamp(ts);
auto* event = packet->set_gpu_mem_total_event();
event->set_gpu_id(gpuId);
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index a0de607..96e6207 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -59,6 +59,9 @@
"libutils",
"libui",
],
+ static_libs: [
+ "libattestation",
+ ],
}
cc_library_shared {
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e49667e..3d99589 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -31,6 +31,25 @@
namespace android {
+static int32_t exceptionCodeFromStatusT(status_t status) {
+ switch (status) {
+ case OK:
+ return binder::Status::EX_NONE;
+ case INVALID_OPERATION:
+ return binder::Status::EX_UNSUPPORTED_OPERATION;
+ case BAD_VALUE:
+ case BAD_TYPE:
+ case NAME_NOT_FOUND:
+ return binder::Status::EX_ILLEGAL_ARGUMENT;
+ case NO_INIT:
+ return binder::Status::EX_ILLEGAL_STATE;
+ case PERMISSION_DENIED:
+ return binder::Status::EX_SECURITY;
+ default:
+ return binder::Status::EX_TRANSACTION_FAILED;
+ }
+}
+
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
@@ -119,7 +138,7 @@
}
// Used by tests only.
-binder::Status InputManager::registerInputChannel(const InputChannel& channel) {
+binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
if (uid != AID_SHELL && uid != AID_ROOT) {
@@ -128,12 +147,17 @@
return binder::Status::ok();
}
- mDispatcher->registerInputChannel(channel.dup());
+ base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
+ if (!channel) {
+ return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
+ channel.error().message().c_str());
+ }
+ (*channel)->copyTo(*outChannel);
return binder::Status::ok();
}
-binder::Status InputManager::unregisterInputChannel(const InputChannel& channel) {
- mDispatcher->unregisterInputChannel(channel);
+binder::Status InputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+ mDispatcher->removeInputChannel(connectionToken);
return binder::Status::ok();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index bf86a98..49bea13 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -108,8 +108,8 @@
const std::vector<InputWindowInfo>& handles,
const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- binder::Status registerInputChannel(const InputChannel& channel) override;
- binder::Status unregisterInputChannel(const InputChannel& channel) 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;
private:
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
new file mode 100644
index 0000000..824c01e
--- /dev/null
+++ b/services/inputflinger/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWindowManagerDeviceTestCases",
+ "options": [
+ {
+ "include-filter": "android.server.wm.WindowInputTests"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 066a816..9abf8b1 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -18,6 +18,7 @@
"libutils",
],
static_libs: [
+ "libattestation",
"libinputdispatcher",
],
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b645d69..c2d165e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -19,6 +19,9 @@
#include <binder/Binder.h>
#include "../dispatcher/InputDispatcher.h"
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
+
namespace android::inputdispatcher {
// An arbitrary device id.
@@ -57,6 +60,8 @@
void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+
void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
@@ -129,17 +134,14 @@
protected:
explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
: mDispatcher(dispatcher) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mServerChannel = std::move(serverChannel);
- mClientChannel = std::move(clientChannel);
+ mClientChannel = *mDispatcher->createInputChannel(name);
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {}
sp<InputDispatcher> mDispatcher;
- std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+ std::shared_ptr<InputChannel> mClientChannel;
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
};
@@ -152,14 +154,12 @@
FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name)
: FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
- mDispatcher->registerInputChannel(mServerChannel);
-
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
}
virtual bool updateInfo() override {
- mInfo.token = mServerChannel->getConnectionToken();
+ mInfo.token = mClientChannel->getConnectionToken();
mInfo.name = "FakeWindowHandle";
mInfo.type = InputWindowInfo::Type::APPLICATION;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
@@ -291,13 +291,13 @@
MotionEvent event = generateMotionEvent();
// Send ACTION_DOWN
dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
// Send ACTION_UP
event.setAction(AMOTION_EVENT_ACTION_UP);
dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
window->consumeEvent();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index d29d8df..ff9aac9 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -48,6 +48,9 @@
"libui",
"libutils",
],
+ static_libs: [
+ "libattestation",
+ ],
header_libs: [
"libinputdispatcher_headers",
],
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 4328e03..34fa239 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -70,12 +70,6 @@
releaseInjectionState();
}
-std::string EventEntry::getDescription() const {
- std::string result;
- appendDescription(result);
- return result;
-}
-
void EventEntry::release() {
refCount -= 1;
if (refCount == 0) {
@@ -99,8 +93,8 @@
ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
-void ConfigurationChangedEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
+std::string ConfigurationChangedEntry::getDescription() const {
+ return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags);
}
// --- DeviceResetEntry ---
@@ -110,8 +104,8 @@
DeviceResetEntry::~DeviceResetEntry() {}
-void DeviceResetEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
+std::string DeviceResetEntry::getDescription() const {
+ return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
}
// --- FocusEntry ---
@@ -126,8 +120,8 @@
FocusEntry::~FocusEntry() {}
-void FocusEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+std::string FocusEntry::getDescription() const {
+ return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
}
// --- KeyEntry ---
@@ -153,12 +147,11 @@
KeyEntry::~KeyEntry() {}
-void KeyEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("KeyEvent");
+std::string KeyEntry::getDescription() const {
if (!GetBoolProperty("ro.debuggable", false)) {
- return;
+ return "KeyEvent";
}
- msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
+ return StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
"flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
deviceId, source, displayId, KeyEvent::actionToString(action), flags,
@@ -212,12 +205,12 @@
MotionEntry::~MotionEntry() {}
-void MotionEntry::appendDescription(std::string& msg) const {
- msg += StringPrintf("MotionEvent");
+std::string MotionEntry::getDescription() const {
if (!GetBoolProperty("ro.debuggable", false)) {
- return;
+ return "MotionEvent";
}
- msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32
+ std::string msg;
+ msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
@@ -235,6 +228,7 @@
pointerCoords[i].getY());
}
msg += StringPrintf("]), policyFlags=0x%08x", policyFlags);
+ return msg;
}
// --- DispatchEntry ---
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index d5b589e..47f75cbe 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -81,9 +81,7 @@
void release();
- virtual void appendDescription(std::string& msg) const = 0;
-
- std::string getDescription() const;
+ virtual std::string getDescription() const = 0;
protected:
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
@@ -93,7 +91,7 @@
struct ConfigurationChangedEntry : EventEntry {
explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
protected:
virtual ~ConfigurationChangedEntry();
@@ -103,7 +101,7 @@
int32_t deviceId;
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
protected:
virtual ~DeviceResetEntry();
@@ -116,7 +114,7 @@
FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
std::string_view reason);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
protected:
virtual ~FocusEntry();
@@ -148,7 +146,7 @@
KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, int32_t repeatCount, nsecs_t downTime);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
void recycle();
protected:
@@ -182,7 +180,7 @@
float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xOffset, float yOffset);
- virtual void appendDescription(std::string& msg) const;
+ std::string getDescription() const override;
protected:
virtual ~MotionEntry();
@@ -263,6 +261,7 @@
std::shared_ptr<InputChannel> inputChannel;
sp<IBinder> oldToken;
sp<IBinder> newToken;
+ std::string obscuringPackage;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index b2d0a26..c8024a6 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -24,7 +24,7 @@
: refCount(1),
injectorPid(injectorPid),
injectorUid(injectorUid),
- injectionResult(INPUT_EVENT_INJECTION_PENDING),
+ injectionResult(android::os::InputEventInjectionResult::PENDING),
injectionIsAsync(false),
pendingForegroundDispatches(0) {}
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 311a0f1..0bfafb1 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -17,34 +17,19 @@
#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
+#include <stdint.h>
#include "InputDispatcherInterface.h"
-#include <stdint.h>
+namespace android {
-namespace android::inputdispatcher {
-
-/*
- * Constants used to determine the input event injection synchronization mode.
- */
-enum {
- /* Injection is asynchronous and is assumed always to be successful. */
- INPUT_EVENT_INJECTION_SYNC_NONE = 0,
-
- /* Waits for previous events to be dispatched so that the input dispatcher can determine
- * whether input event injection willbe permitted based on the current input focus.
- * Does not wait for the input event to finish processing. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
-
- /* Waits for the input event to be completely processed. */
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
-};
+namespace inputdispatcher {
struct InjectionState {
mutable int32_t refCount;
int32_t injectorPid;
int32_t injectorUid;
- int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+ android::os::InputEventInjectionResult injectionResult; // initially PENDING
bool injectionIsAsync; // set to true if injection is not waiting for the result
int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
@@ -55,6 +40,7 @@
~InjectionState();
};
-} // namespace android::inputdispatcher
+} // namespace inputdispatcher
+} // namespace android
#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 078448f..d7aea4e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -28,8 +28,8 @@
// Log debug messages about the dispatch cycle.
#define DEBUG_DISPATCH_CYCLE 0
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
+// Log debug messages about channel creation
+#define DEBUG_CHANNEL_CREATION 0
// Log debug messages about input event injection.
#define DEBUG_INJECTION 0
@@ -53,8 +53,6 @@
#include <input/InputWindow.h>
#include <log/log.h>
#include <log/log_event_list.h>
-#include <openssl/hmac.h>
-#include <openssl/rand.h>
#include <powermanager/PowerManager.h>
#include <statslog.h>
#include <unistd.h>
@@ -76,6 +74,9 @@
#define INDENT4 " "
using android::base::StringPrintf;
+using android::os::BlockUntrustedTouchesMode;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
namespace android::inputdispatcher {
@@ -219,6 +220,36 @@
}
}
+static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+ constexpr size_t maxEntries = 50; // max events to print
+ constexpr size_t skipBegin = maxEntries / 2;
+ const size_t skipEnd = queue.size() - maxEntries / 2;
+ // skip from maxEntries / 2 ... size() - maxEntries/2
+ // only print from 0 .. skipBegin and then from skipEnd .. size()
+
+ std::string dump;
+ for (size_t i = 0; i < queue.size(); i++) {
+ const DispatchEntry& entry = *queue[i];
+ if (i >= skipBegin && i < skipEnd) {
+ dump += StringPrintf(INDENT4 "<skipped %zu entries>\n", skipEnd - skipBegin);
+ i = skipEnd - 1; // it will be incremented to "skipEnd" by 'continue'
+ continue;
+ }
+ 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,
+ ns2ms(currentTime - entry.eventEntry->eventTime));
+ if (entry.deliveryTime != 0) {
+ // This entry was delivered, so add information on how long we've been waiting
+ dump += StringPrintf(", wait=%" PRId64 "ms", ns2ms(currentTime - entry.deliveryTime));
+ }
+ dump.append("\n");
+ }
+ return dump;
+}
+
/**
* Find the entry in std::unordered_map by key, and return it.
* If the entry is not found, return a default constructed entry.
@@ -254,6 +285,15 @@
return removed;
}
+/**
+ * Find the entry in std::unordered_map by key and return the value as an optional.
+ */
+template <typename K, typename V>
+static std::optional<V> getOptionalValueByKey(const std::unordered_map<K, V>& map, K key) {
+ auto it = map.find(key);
+ return it != map.end() ? std::optional<V>{it->second} : std::nullopt;
+}
+
static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
if (first == second) {
return true;
@@ -344,51 +384,38 @@
}
}
-static std::array<uint8_t, 128> getRandomKey() {
- std::array<uint8_t, 128> key;
- if (RAND_bytes(key.data(), key.size()) != 1) {
- LOG_ALWAYS_FATAL("Can't generate HMAC key");
- }
- return key;
+static status_t openInputChannelPair(const std::string& name,
+ std::shared_ptr<InputChannel>& serverChannel,
+ std::unique_ptr<InputChannel>& clientChannel) {
+ std::unique_ptr<InputChannel> uniqueServerChannel;
+ status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
+
+ serverChannel = std::move(uniqueServerChannel);
+ return result;
}
-// --- HmacKeyManager ---
-
-HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
-
-std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
- size_t size;
- switch (event.type) {
- case VerifiedInputEvent::Type::KEY: {
- size = sizeof(VerifiedKeyEvent);
- break;
- }
- case VerifiedInputEvent::Type::MOTION: {
- size = sizeof(VerifiedMotionEvent);
- break;
- }
+const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
+ switch (result) {
+ case InputDispatcher::FocusResult::OK:
+ return "Ok";
+ case InputDispatcher::FocusResult::NO_WINDOW:
+ return "Window not found";
+ case InputDispatcher::FocusResult::NOT_FOCUSABLE:
+ return "Window not focusable";
+ case InputDispatcher::FocusResult::NOT_VISIBLE:
+ return "Window not visible";
}
- const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
- return sign(start, size);
}
-std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
- // SHA256 always generates 32-bytes result
- std::array<uint8_t, 32> hash;
- unsigned int hashLen = 0;
- uint8_t* result =
- HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
- if (result == nullptr) {
- ALOGE("Could not sign the data using HMAC");
- return INVALID_HMAC;
+template <typename T>
+static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+ if (lhs == nullptr && rhs == nullptr) {
+ return true;
}
-
- if (hashLen != hash.size()) {
- ALOGE("HMAC-SHA256 has unexpected length");
- return INVALID_HMAC;
+ if (lhs == nullptr || rhs == nullptr) {
+ return false;
}
-
- return hash;
+ return *lhs == *rhs;
}
// --- InputDispatcher ---
@@ -408,6 +435,7 @@
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
+ mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -428,7 +456,7 @@
while (!mConnectionsByFd.empty()) {
sp<Connection> connection = mConnectionsByFd.begin()->second;
- unregisterInputChannel(*connection->inputChannel);
+ removeInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -487,6 +515,33 @@
}
/**
+ * Raise ANR if there is no focused window.
+ * Before the ANR is raised, do a final state check:
+ * 1. The currently focused application must be the same one we are waiting for.
+ * 2. Ensure we still don't have a focused window.
+ */
+void InputDispatcher::processNoFocusedWindowAnrLocked() {
+ // Check if the application that we are waiting for is still focused.
+ std::shared_ptr<InputApplicationHandle> focusedApplication =
+ getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
+ if (focusedApplication == nullptr ||
+ focusedApplication->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
+ // Unexpected because we should have reset the ANR timer when focused application changed
+ ALOGE("Waited for a focused window, but focused application has already changed to %s",
+ focusedApplication->getName().c_str());
+ return; // The focused application has changed.
+ }
+
+ const sp<InputWindowHandle>& focusedWindowHandle =
+ getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
+ if (focusedWindowHandle != nullptr) {
+ return; // We now have a focused window. No need for ANR.
+ }
+ onAnrLocked(mAwaitedFocusedApplication);
+}
+
+/**
* Check if any of the connections' wait queues have events that are too old.
* If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
* Return the time at which we should wake up next.
@@ -497,8 +552,9 @@
// 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) {
- onAnrLocked(mAwaitedFocusedApplication);
+ processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.reset();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
// Keep waiting
@@ -523,7 +579,7 @@
connection->responsive = false;
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
- onAnrLocked(connection);
+ onAnrLocked(*connection);
return LONG_LONG_MIN;
}
@@ -813,7 +869,7 @@
"Must provide a valid touch state if adding portal windows or outside targets");
}
// Traverse windows from front to back to find touched window.
- const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
@@ -995,11 +1051,11 @@
void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
InjectionState* injectionState = entry->injectionState;
- if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Injected inbound event was dropped.");
#endif
- setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(entry, InputEventInjectionResult::FAILED);
}
if (entry == mNextUnblockedEvent) {
mNextUnblockedEvent = nullptr;
@@ -1078,7 +1134,7 @@
return true;
}
-void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus,
+void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
std::string_view reason) {
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
@@ -1088,7 +1144,7 @@
}
FocusEntry* focusEntry =
- new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus, reason);
+ new FocusEntry(mIdGenerator.nextId(), now(), windowToken, hasFocus, reason);
// This event should go to the front of the queue, but behind all other focus events
// Find the last focus event, and insert right after it
@@ -1124,11 +1180,17 @@
(entry->policyFlags & POLICY_FLAG_TRUSTED) &&
(!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry &&
- mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode &&
// We have seen two identical key downs in a row which indicates that the device
// driver is automatically generating key repeats itself. We take note of the
// repeat here, but we disable our own next key repeat timer since it is clear that
// we will not need to synthesize key repeats ourselves.
+ mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) {
+ // Make sure we don't get key down from a different device. If a different
+ // device Id has same key pressed down, the new device Id will replace the
+ // current one to hold the key repeat with repeat count reset.
+ // In the future when got a KEY_UP on the device id, drop it and do not
+ // stop the key repeat on current device.
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
@@ -1139,6 +1201,12 @@
}
mKeyRepeatState.lastKeyEntry = entry;
entry->refCount += 1;
+ } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
+ mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
+ // The stale device releases the key, reset staleDeviceId.
+#if DEBUG_INBOUND_EVENT_DETAILS
+ ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+#endif
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -1171,10 +1239,10 @@
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
- if (focusedWindowHandle != nullptr) {
- commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
+ sp<IBinder> focusedWindowToken =
+ getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
+ if (focusedWindowToken != nullptr) {
+ commandEntry->inputChannel = getInputChannelLocked(focusedWindowToken);
}
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
@@ -1192,22 +1260,22 @@
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
setInjectionResult(entry,
- *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
- : INPUT_EVENT_INJECTION_FAILED);
+ *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
+ : InputEventInjectionResult::FAILED);
mReporter->reportDroppedKey(entry->id);
return true;
}
// Identify targets.
std::vector<InputTarget> inputTargets;
- int32_t injectionResult =
+ InputEventInjectionResult injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
- if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
setInjectionResult(entry, injectionResult);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
return true;
}
@@ -1243,8 +1311,8 @@
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
setInjectionResult(entry,
- *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
- : INPUT_EVENT_INJECTION_FAILED);
+ *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
+ : InputEventInjectionResult::FAILED);
return true;
}
@@ -1254,7 +1322,7 @@
std::vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
- int32_t injectionResult;
+ InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult =
@@ -1265,16 +1333,16 @@
injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
}
- if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
setInjectionResult(entry, injectionResult);
- if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
+ if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {
ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
return true;
}
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
CancelationOptions::Mode mode(isPointerEvent
? CancelationOptions::CANCEL_POINTER_EVENTS
: CancelationOptions::CANCEL_NON_POINTER_EVENTS);
@@ -1456,15 +1524,13 @@
return false;
}
-int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
- const EventEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) {
+InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked(
+ nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime) {
std::string reason;
int32_t displayId = getTargetDisplayId(entry);
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
@@ -1474,7 +1540,7 @@
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
EventEntry::typeToString(entry.type), displayId);
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
// Compatibility behavior: raise ANR if there is a focused application, but no focused window.
@@ -1489,19 +1555,20 @@
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
mAwaitedFocusedApplication = focusedApplicationHandle;
+ mAwaitedApplicationDisplayId = displayId;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
*nextWakeupTime = *mNoFocusedWindowTimeoutTime;
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
EventEntry::typeToString(entry.type));
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
} else {
// Still waiting for the focused window
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
}
}
@@ -1510,12 +1577,12 @@
// Check permissions.
if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
- return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ return InputEventInjectionResult::PERMISSION_DENIED;
}
if (focusedWindowHandle->getInfo()->paused) {
ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
}
// If the event is a key event, then we must wait for all previous events to
@@ -1532,7 +1599,7 @@
if (entry.type == EventEntry::Type::KEY) {
if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
- return INPUT_EVENT_INJECTION_PENDING;
+ return InputEventInjectionResult::PENDING;
}
}
@@ -1542,7 +1609,7 @@
BitSet32(0), inputTargets);
// Done.
- return INPUT_EVENT_INJECTION_SUCCEEDED;
+ return InputEventInjectionResult::SUCCEEDED;
}
/**
@@ -1571,11 +1638,9 @@
return responsiveMonitors;
}
-int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
- const MotionEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) {
+InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
+ nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
+ nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
ATRACE_CALL();
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
@@ -1590,7 +1655,7 @@
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
- int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle);
sp<InputWindowHandle> newTouchedWindowHandle;
@@ -1625,7 +1690,7 @@
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -1641,7 +1706,7 @@
"in display %" PRId32,
displayId);
// TODO: test multiple simultaneous input streams.
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
switchedDevice = false;
wrongDevice = true;
goto Failed;
@@ -1703,6 +1768,21 @@
}
}
+ // Drop events that can't be trusted due to occlusion
+ if (newTouchedWindowHandle != nullptr &&
+ mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+ TouchOcclusionInfo occlusionInfo =
+ computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
+ if (!isTouchTrustedLocked(occlusionInfo)) {
+ onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
+ if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+ ALOGW("Dropping untrusted touch event due to %s/%d",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+ newTouchedWindowHandle = nullptr;
+ }
+ }
+ }
+
// Also don't send the new touch event to unresponsive gesture monitors
newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
@@ -1710,7 +1790,7 @@
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
"(%d, %d) in display %" PRId32 ".",
x, y, displayId);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -1753,7 +1833,7 @@
"dropped the pointer down event in display %" PRId32,
displayId);
}
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -1838,7 +1918,7 @@
if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
haveForegroundWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
goto Failed;
}
@@ -1849,7 +1929,7 @@
ALOGI("Dropping event because there is no touched foreground window in display "
"%" PRId32 " or gesture monitor to receive it.",
displayId);
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ injectionResult = InputEventInjectionResult::FAILED;
goto Failed;
}
@@ -1887,7 +1967,7 @@
sp<InputWindowHandle> foregroundWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
- const std::vector<sp<InputWindowHandle>> windowHandles =
+ const std::vector<sp<InputWindowHandle>>& windowHandles =
getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
const InputWindowInfo* info = windowHandle->getInfo();
@@ -1906,7 +1986,7 @@
}
// Success! Output targets.
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ injectionResult = InputEventInjectionResult::SUCCEEDED;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
@@ -2104,9 +2184,9 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
- } else if (info->ownerPid == otherInfo->ownerPid) {
- // If ownerPid is the same we don't generate occlusion events as there
- // is no in-process security boundary.
+ } else if (info->ownerUid == otherInfo->ownerUid) {
+ // If ownerUid is the same we don't generate occlusion events as there
+ // is no security boundary within an uid.
return false;
} else if (otherInfo->trustedOverlay) {
return false;
@@ -2116,10 +2196,88 @@
return true;
}
+/**
+ * Returns touch occlusion information in the form of TouchOcclusionInfo. To check if the touch is
+ * untrusted, one should check:
+ *
+ * 1. If result.hasBlockingOcclusion is true.
+ * If it's, it means the touch should be blocked due to a window with occlusion mode of
+ * BLOCK_UNTRUSTED.
+ *
+ * 2. If result.obscuringOpacity > mMaximumObscuringOpacityForTouch.
+ * If it is (and 1 is false), then the touch should be blocked because a stack of windows
+ * (possibly only one) with occlusion mode of USE_OPACITY from one UID resulted in a composed
+ * obscuring opacity above the threshold. Note that if there was no window of occlusion mode
+ * USE_OPACITY, result.obscuringOpacity would've been 0 and since
+ * mMaximumObscuringOpacityForTouch >= 0, the condition above would never be true.
+ *
+ * If neither of those is true, then it means the touch can be allowed.
+ */
+InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+ const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+ int32_t displayId = windowHandle->getInfo()->displayId;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ TouchOcclusionInfo info;
+ info.hasBlockingOcclusion = false;
+ info.obscuringOpacity = 0;
+ info.obscuringUid = -1;
+ std::map<int32_t, float> opacityByUid;
+ for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+ if (windowHandle == otherHandle) {
+ break; // All future windows are below us. Exit early.
+ }
+ const InputWindowInfo* otherInfo = otherHandle->getInfo();
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
+ windowHandle->getInfo()->ownerUid != otherInfo->ownerUid &&
+ otherInfo->frameContainsPoint(x, y)) {
+ // canBeObscuredBy() has returned true above, which means this window is untrusted, so
+ // we perform the checks below to see if the touch can be propagated or not based on the
+ // window's touch occlusion mode
+ if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) {
+ info.hasBlockingOcclusion = true;
+ info.obscuringUid = otherInfo->ownerUid;
+ info.obscuringPackage = otherInfo->packageName;
+ break;
+ }
+ if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
+ uint32_t uid = otherInfo->ownerUid;
+ float opacity =
+ (opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
+ // Given windows A and B:
+ // opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)]
+ opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha);
+ opacityByUid[uid] = opacity;
+ if (opacity > info.obscuringOpacity) {
+ info.obscuringOpacity = opacity;
+ info.obscuringUid = uid;
+ info.obscuringPackage = otherInfo->packageName;
+ }
+ }
+ }
+ }
+ return info;
+}
+
+bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+ if (occlusionInfo.hasBlockingOcclusion) {
+ ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid);
+ return false;
+ }
+ if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
+ ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+ "%.2f, maximum allowed = %.2f)",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+ occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
+ return false;
+ }
+ return true;
+}
+
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
int32_t x, int32_t y) const {
int32_t displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
@@ -2135,7 +2293,7 @@
bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
int32_t displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
@@ -2172,8 +2330,7 @@
return;
}
int32_t displayId = getTargetDisplayId(eventEntry);
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
if (focusedWindowHandle != nullptr) {
const InputWindowInfo* info = focusedWindowHandle->getInfo();
if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
@@ -2510,30 +2667,22 @@
}
void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
- const sp<IBinder>& newToken) {
+ const sp<IBinder>& token) {
int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
return;
}
- sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
- if (inputWindowHandle == nullptr) {
- return;
- }
-
- sp<InputWindowHandle> focusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
-
- bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
-
- if (!hasFocusChanged) {
+ sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ if (focusedToken == token) {
+ // ignore since token is focused
return;
}
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
- commandEntry->newToken = newToken;
+ commandEntry->newToken = token;
postCommandLocked(std::move(commandEntry));
}
@@ -2692,6 +2841,22 @@
}
}
+std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) const {
+ size_t size;
+ switch (event.type) {
+ case VerifiedInputEvent::Type::KEY: {
+ size = sizeof(VerifiedKeyEvent);
+ break;
+ }
+ case VerifiedInputEvent::Type::MOTION: {
+ size = sizeof(VerifiedMotionEvent);
+ break;
+ }
+ }
+ const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
+ return mHmacKeyManager.sign(start, size);
+}
+
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
@@ -2701,7 +2866,7 @@
VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
verifiedEvent.actionMasked = actionMasked;
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
- return mHmacKeyManager.sign(verifiedEvent);
+ return sign(verifiedEvent);
}
return INVALID_HMAC;
}
@@ -2711,7 +2876,7 @@
VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
verifiedEvent.action = dispatchEntry.resolvedAction;
- return mHmacKeyManager.sign(verifiedEvent);
+ return sign(verifiedEvent);
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
@@ -2835,8 +3000,8 @@
}
}
- // Unregister the channel.
- d->unregisterInputChannelLocked(*connection->inputChannel, notify);
+ // Remove the channel.
+ d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
return 0; // remove the callback
} // release lock
}
@@ -3348,15 +3513,14 @@
}
}
-int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode,
- std::chrono::milliseconds timeout, uint32_t policyFlags) {
+InputEventInjectionResult InputDispatcher::injectInputEvent(
+ const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
"syncMode=%d, timeout=%lld, policyFlags=0x%08x",
event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
#endif
-
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
policyFlags |= POLICY_FLAG_INJECTED;
@@ -3370,7 +3534,7 @@
const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
int32_t action = incomingKey.getAction();
if (!validateKeyEvent(action)) {
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
int32_t flags = incomingKey.getFlags();
@@ -3416,7 +3580,7 @@
int32_t actionButton = motionEvent->getActionButton();
int32_t displayId = motionEvent->getDisplayId();
if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
@@ -3469,11 +3633,11 @@
default:
ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
- return INPUT_EVENT_INJECTION_FAILED;
+ return InputEventInjectionResult::FAILED;
}
InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
- if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ if (syncMode == InputEventInjectionSync::NONE) {
injectionState->injectionIsAsync = true;
}
@@ -3492,16 +3656,16 @@
mLooper->wake();
}
- int32_t injectionResult;
+ InputEventInjectionResult injectionResult;
{ // acquire lock
std::unique_lock _l(mLock);
- if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ if (syncMode == InputEventInjectionSync::NONE) {
+ injectionResult = InputEventInjectionResult::SUCCEEDED;
} else {
for (;;) {
injectionResult = injectionState->injectionResult;
- if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+ if (injectionResult != InputEventInjectionResult::PENDING) {
break;
}
@@ -3511,15 +3675,15 @@
ALOGD("injectInputEvent - Timed out waiting for injection result "
"to become available.");
#endif
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED &&
- syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+ if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
+ syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
while (injectionState->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
@@ -3531,7 +3695,7 @@
ALOGD("injectInputEvent - Timed out waiting for pending foreground "
"dispatches to finish.");
#endif
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -3559,7 +3723,7 @@
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event);
VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent);
result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent);
- calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent);
+ calculatedHmac = sign(verifiedKeyEvent);
break;
}
case AINPUT_EVENT_TYPE_MOTION: {
@@ -3567,7 +3731,7 @@
VerifiedMotionEvent verifiedMotionEvent =
verifiedMotionEventFromMotionEvent(motionEvent);
result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
- calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+ calculatedHmac = sign(verifiedMotionEvent);
break;
}
default: {
@@ -3589,7 +3753,8 @@
mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}
-void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
+void InputDispatcher::setInjectionResult(EventEntry* entry,
+ InputEventInjectionResult injectionResult) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
#if DEBUG_INJECTION
@@ -3601,18 +3766,21 @@
if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
- case INPUT_EVENT_INJECTION_SUCCEEDED:
+ case InputEventInjectionResult::SUCCEEDED:
ALOGV("Asynchronous input event injection succeeded.");
break;
- case INPUT_EVENT_INJECTION_FAILED:
+ case InputEventInjectionResult::FAILED:
ALOGW("Asynchronous input event injection failed.");
break;
- case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ case InputEventInjectionResult::PERMISSION_DENIED:
ALOGW("Asynchronous input event injection permission denied.");
break;
- case INPUT_EVENT_INJECTION_TIMED_OUT:
+ case InputEventInjectionResult::TIMED_OUT:
ALOGW("Asynchronous input event injection timed out.");
break;
+ case InputEventInjectionResult::PENDING:
+ ALOGE("Setting result to 'PENDING' for asynchronous injection");
+ break;
}
}
@@ -3639,9 +3807,11 @@
}
}
-std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
+const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked(
int32_t displayId) const {
- return getValueByKey(mWindowHandlesByDisplay, displayId);
+ static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES;
+ auto it = mWindowHandlesByDisplay.find(displayId);
+ return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
}
sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
@@ -3651,7 +3821,7 @@
}
for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
if (windowHandle->getToken() == windowHandleToken) {
return windowHandle;
@@ -3661,9 +3831,28 @@
return nullptr;
}
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+ int displayId) const {
+ if (windowHandleToken == nullptr) {
+ return nullptr;
+ }
+
+ for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
+ }
+ return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+ sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ return getWindowHandleLocked(focusedToken, displayId);
+}
+
bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
for (const sp<InputWindowHandle>& handle : windowHandles) {
if (handle->getId() == windowHandle->getId() &&
handle->getToken() == windowHandle->getToken()) {
@@ -3814,29 +4003,31 @@
updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
- sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
- bool foundHoveredWindow = false;
- for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
- // Set newFocusedWindowHandle to the top most focused window instead of the last one
- if (!newFocusedWindowHandle && windowHandle->getInfo()->focusable &&
- windowHandle->getInfo()->visible) {
- newFocusedWindowHandle = windowHandle;
- }
- if (windowHandle == mLastHoverWindowHandle) {
- foundHoveredWindow = true;
- }
- }
-
- if (!foundHoveredWindow) {
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ if (mLastHoverWindowHandle &&
+ std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
+ windowHandles.end()) {
mLastHoverWindowHandle = nullptr;
}
- sp<InputWindowHandle> oldFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ if (focusedToken) {
+ FocusResult result = checkTokenFocusableLocked(focusedToken, displayId);
+ if (result != FocusResult::OK) {
+ onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result));
+ }
+ }
- if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
- onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle, displayId,
- "setInputWindowsLocked");
+ std::optional<FocusRequest> focusRequest =
+ getOptionalValueByKey(mPendingFocusRequests, displayId);
+ if (focusRequest) {
+ // If the window from the pending request is now visible, provide it focus.
+ FocusResult result = handleFocusRequestLocked(*focusRequest);
+ if (result != FocusResult::NOT_VISIBLE) {
+ // Drop the request if we were able to change the focus or we cannot change
+ // it for another reason.
+ mPendingFocusRequests.erase(displayId);
+ }
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -3884,31 +4075,26 @@
ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
}
- if (inputApplicationHandle != nullptr &&
- inputApplicationHandle->getApplicationToken() != nullptr) {
- // acquire lock
+ { // acquire lock
std::scoped_lock _l(mLock);
std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
- // If oldFocusedApplicationHandle already exists
- if (oldFocusedApplicationHandle != nullptr) {
- // If a new focused application handle is different from the old one and
- // old focus application info is awaited focused application info.
- if (*oldFocusedApplicationHandle != *inputApplicationHandle &&
- mAwaitedFocusedApplication != nullptr &&
- *oldFocusedApplicationHandle == *mAwaitedFocusedApplication) {
- resetNoFocusedWindowTimeoutLocked();
- }
- // Erase the old application from container first
- mFocusedApplicationHandlesByDisplay.erase(displayId);
- // Should already get freed after removed from container but just double check.
- oldFocusedApplicationHandle.reset();
+ if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
+ return; // This application is already focused. No need to wake up or change anything.
}
// Set the new application handle.
- mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
+ if (inputApplicationHandle != nullptr) {
+ mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
+ } else {
+ mFocusedApplicationHandlesByDisplay.erase(displayId);
+ }
+
+ // No matter what the old focused application was, stop waiting on it because it is
+ // no longer focused.
+ resetNoFocusedWindowTimeoutLocked();
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -3932,11 +4118,11 @@
std::scoped_lock _l(mLock);
if (mFocusedDisplayId != displayId) {
- sp<InputWindowHandle> oldFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
- if (oldFocusedWindowHandle != nullptr) {
+ sp<IBinder> oldFocusedWindowToken =
+ getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ if (oldFocusedWindowToken != nullptr) {
std::shared_ptr<InputChannel> inputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
+ getInputChannelLocked(oldFocusedWindowToken);
if (inputChannel != nullptr) {
CancelationOptions
options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
@@ -3948,19 +4134,15 @@
mFocusedDisplayId = displayId;
// Find new focused window and validate
- sp<InputWindowHandle> newFocusedWindowHandle =
- getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ sp<IBinder> newFocusedWindowToken =
+ getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
- if (newFocusedWindowHandle == nullptr) {
+ if (newFocusedWindowToken == nullptr) {
ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
- if (!mFocusedWindowHandlesByDisplay.empty()) {
- ALOGE("But another display has a focused window:");
- for (auto& it : mFocusedWindowHandlesByDisplay) {
- const sp<InputWindowHandle>& windowHandle = it.second;
- ALOGE("Display #%" PRId32 " has focused window: '%s'\n", it.first,
- windowHandle->getName().c_str());
- }
+ if (!mFocusedWindowTokenByDisplay.empty()) {
+ ALOGE("But another display has a focused window\n%s",
+ dumpFocusedWindowsLocked().c_str());
}
}
}
@@ -4035,6 +4217,21 @@
mInTouchMode = inTouchMode;
}
+void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
+ if (opacity < 0 || opacity > 1) {
+ LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+ return;
+ }
+
+ std::scoped_lock lock(mLock);
+ mMaximumObscuringOpacityForTouch = opacity;
+}
+
+void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) {
+ std::scoped_lock lock(mLock);
+ mBlockUntrustedTouchesMode = mode;
+}
+
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
@@ -4145,6 +4342,28 @@
}
}
+std::string InputDispatcher::dumpFocusedWindowsLocked() {
+ if (mFocusedWindowTokenByDisplay.empty()) {
+ return INDENT "FocusedWindows: <none>\n";
+ }
+
+ std::string dump;
+ dump += INDENT "FocusedWindows:\n";
+ for (auto& it : mFocusedWindowTokenByDisplay) {
+ const int32_t displayId = it.first;
+ const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId);
+ if (windowHandle) {
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ windowHandle->getName().c_str());
+ } else {
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32
+ " has focused token without a window'\n",
+ displayId);
+ }
+ }
+ return dump;
+}
+
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4166,17 +4385,7 @@
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
}
- if (!mFocusedWindowHandlesByDisplay.empty()) {
- dump += StringPrintf(INDENT "FocusedWindows:\n");
- for (auto& it : mFocusedWindowHandlesByDisplay) {
- const int32_t displayId = it.first;
- const sp<InputWindowHandle>& windowHandle = it.second;
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- windowHandle->getName().c_str());
- }
- } else {
- dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
- }
+ dump += dumpFocusedWindowsLocked();
if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4225,6 +4434,7 @@
"hasWallpaper=%s, visible=%s, "
"flags=%s, type=0x%08x, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
+ "applicationInfo=%s, "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
windowInfo->portalToDisplayId,
@@ -4236,7 +4446,8 @@
static_cast<int32_t>(windowInfo->type),
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->globalScaleFactor);
+ windowInfo->globalScaleFactor,
+ windowInfo->applicationInfo.name.c_str());
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
@@ -4276,7 +4487,7 @@
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
for (EventEntry* entry : mRecentQueue) {
dump += INDENT2;
- entry->appendDescription(dump);
+ dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
@@ -4287,7 +4498,7 @@
if (mPendingEvent) {
dump += INDENT "PendingEvent:\n";
dump += INDENT2;
- mPendingEvent->appendDescription(dump);
+ dump += mPendingEvent->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n",
ns2ms(currentTime - mPendingEvent->eventTime));
} else {
@@ -4299,7 +4510,7 @@
dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
for (EventEntry* entry : mInboundQueue) {
dump += INDENT2;
- entry->appendDescription(dump);
+ dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
}
} else {
@@ -4331,14 +4542,8 @@
if (!connection->outboundQueue.empty()) {
dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
connection->outboundQueue.size());
- for (DispatchEntry* entry : connection->outboundQueue) {
- dump.append(INDENT4);
- entry->eventEntry->appendDescription(dump);
- dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
- "ms\n",
- entry->targetFlags, entry->resolvedAction,
- ns2ms(currentTime - entry->eventEntry->eventTime));
- }
+ dump += dumpQueue(connection->outboundQueue, currentTime);
+
} else {
dump += INDENT3 "OutboundQueue: <empty>\n";
}
@@ -4346,15 +4551,7 @@
if (!connection->waitQueue.empty()) {
dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
connection->waitQueue.size());
- for (DispatchEntry* entry : connection->waitQueue) {
- dump += INDENT4;
- entry->eventEntry->appendDescription(dump);
- dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
- "age=%" PRId64 "ms, wait=%" PRId64 "ms seq=%" PRIu32 "\n",
- entry->targetFlags, entry->resolvedAction,
- ns2ms(currentTime - entry->eventEntry->eventTime),
- ns2ms(currentTime - entry->deliveryTime), entry->seq);
- }
+ dump += dumpQueue(connection->waitQueue, currentTime);
} else {
dump += INDENT3 "WaitQueue: <empty>\n";
}
@@ -4386,75 +4583,76 @@
}
}
-status_t InputDispatcher::registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
+ const std::string& name) {
+#if DEBUG_CHANNEL_CREATION
+ ALOGD("channel '%s' ~ createInputChannel", name.c_str());
#endif
+ std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+
+ if (result) {
+ return base::Error(result) << "Failed to open input channel pair with name " << name;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
- if (existingConnection != nullptr) {
- ALOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().c_str());
- return BAD_VALUE;
- }
+ sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
- sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
-
- int fd = inputChannel->getFd();
+ int fd = serverChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+ mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
- return OK;
+ return clientChannel;
}
-status_t InputDispatcher::registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) {
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
+ int32_t displayId, bool isGestureMonitor, const std::string& name) {
+ std::shared_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+ if (result) {
+ return base::Error(result) << "Failed to open input channel pair with name " << name;
+ }
+
{ // acquire lock
std::scoped_lock _l(mLock);
if (displayId < 0) {
- ALOGW("Attempted to register input monitor without a specified display.");
- return BAD_VALUE;
+ return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
+ << " without a specified display.";
}
- if (inputChannel->getConnectionToken() == nullptr) {
- ALOGW("Attempted to register input monitor without an identifying token.");
- return BAD_VALUE;
- }
+ sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
- sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
-
- const int fd = inputChannel->getFd();
+ const int fd = serverChannel->getFd();
mConnectionsByFd[fd] = connection;
- mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+ mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
auto& monitorsByDisplay =
isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
- monitorsByDisplay[displayId].emplace_back(inputChannel);
+ monitorsByDisplay[displayId].emplace_back(serverChannel);
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
}
+
// Wake the looper because some connections have changed.
mLooper->wake();
- return OK;
+ return clientChannel;
}
-status_t InputDispatcher::unregisterInputChannel(const InputChannel& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel.getName().c_str());
-#endif
-
+status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
- status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+ status_t status = removeInputChannelLocked(connectionToken, false /*notify*/);
if (status) {
return status;
}
@@ -4466,23 +4664,22 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannelLocked(const InputChannel& inputChannel,
- bool notify) {
- sp<Connection> connection = getConnectionLocked(inputChannel.getConnectionToken());
+status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+ bool notify) {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
- ALOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel.getName().c_str());
+ ALOGW("Attempted to unregister already unregistered input channel");
return BAD_VALUE;
}
removeConnectionLocked(connection);
- mInputChannelsByToken.erase(inputChannel.getConnectionToken());
+ mInputChannelsByToken.erase(connectionToken);
if (connection->monitor) {
- removeMonitorChannelLocked(inputChannel);
+ removeMonitorChannelLocked(connectionToken);
}
- mLooper->removeFd(inputChannel.getFd());
+ mLooper->removeFd(connection->inputChannel->getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -4491,19 +4688,19 @@
return OK;
}
-void InputDispatcher::removeMonitorChannelLocked(const InputChannel& inputChannel) {
- removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay);
- removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay);
+void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) {
+ removeMonitorChannelLocked(connectionToken, mGlobalMonitorsByDisplay);
+ removeMonitorChannelLocked(connectionToken, mGestureMonitorsByDisplay);
}
void InputDispatcher::removeMonitorChannelLocked(
- const InputChannel& inputChannel,
+ const sp<IBinder>& connectionToken,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) {
for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) {
std::vector<Monitor>& monitors = it->second;
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
- if (*monitors[i].inputChannel == inputChannel) {
+ if (monitors[i].inputChannel->getConnectionToken() == connectionToken) {
monitors.erase(monitors.begin() + i);
break;
}
@@ -4622,10 +4819,8 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::notifyFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) {
- sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
- sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
+void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
+ const sp<IBinder>& newToken) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doNotifyFocusChangedLockedInterruptible);
commandEntry->oldToken = oldToken;
@@ -4633,12 +4828,12 @@
postCommandLocked(std::move(commandEntry));
}
-void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::onAnrLocked(const Connection& connection) {
// Since we are allowing the policy to extend the timeout, maybe the waitQueue
// is already healthy again. Don't raise ANR in this situation
- if (connection->waitQueue.empty()) {
+ if (connection.waitQueue.empty()) {
ALOGI("Not raising ANR because the connection %s has recovered",
- connection->inputChannel->getName().c_str());
+ connection.inputChannel->getName().c_str());
return;
}
/**
@@ -4649,21 +4844,21 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+ DispatchEntry* oldestEntry = *connection.waitQueue.begin();
const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
- connection->inputChannel->getName().c_str(),
+ connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
- updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+ updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection->inputChannel;
+ commandEntry->inputChannel = connection.inputChannel;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4682,6 +4877,13 @@
postCommandLocked(std::move(commandEntry));
}
+void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
+ std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+ &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
+ commandEntry->obscuringPackage = obscuringPackage;
+ postCommandLocked(std::move(commandEntry));
+}
+
void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
const std::string& reason) {
const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
@@ -4760,18 +4962,26 @@
}
}
+void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
+
+ mLock.lock();
+}
+
void InputDispatcher::extendAnrTimeoutsLocked(
const std::shared_ptr<InputApplicationHandle>& application,
const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
+ if (connectionToken == nullptr && application != nullptr) {
+ // The ANR happened because there's no focused window
+ mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
+ mAwaitedFocusedApplication = application;
+ }
+
sp<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
- if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
- // Maybe ANR happened because there's no focused window?
- mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
- mAwaitedFocusedApplication = application;
- } else {
- // It's also possible that the connection already disappeared. No action necessary.
- }
+ // It's possible that the connection already disappeared. No action necessary.
return;
}
@@ -5202,37 +5412,132 @@
* when requesting the focus change. This determines which request gets
* precedence if there is a focus change request from another source such as pointer down.
*/
-void InputDispatcher::setFocusedWindow(const FocusRequest& request) {}
+void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
-void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocusedWindowHandle,
- const sp<InputWindowHandle>& newFocusedWindowHandle,
- int32_t displayId, std::string_view reason) {
- if (oldFocusedWindowHandle) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus left window: %s in display %" PRId32,
- oldFocusedWindowHandle->getName().c_str(), displayId);
+ const int32_t displayId = request.displayId;
+ const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ if (request.focusedToken && oldFocusedToken != request.focusedToken) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow on display %" PRId32
+ " ignored, reason: focusedToken is not focused",
+ displayId);
+ return;
}
- std::shared_ptr<InputChannel> focusedInputChannel =
- getInputChannelLocked(oldFocusedWindowHandle->getToken());
+
+ mPendingFocusRequests.erase(displayId);
+ FocusResult result = handleFocusRequestLocked(request);
+ if (result == FocusResult::NOT_VISIBLE) {
+ // The requested window is not currently visible. Wait for the window to become visible
+ // and then provide it focus. This is to handle situations where a user action triggers
+ // a new window to appear. We want to be able to queue any key events after the user
+ // action and deliver it to the newly focused window. In order for this to happen, we
+ // take focus from the currently focused window so key events can be queued.
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow on display %" PRId32
+ " pending, reason: window is not visible",
+ displayId);
+ mPendingFocusRequests[displayId] = request;
+ onFocusChangedLocked(oldFocusedToken, nullptr, displayId,
+ "setFocusedWindow_AwaitingWindowVisibility");
+ } else if (result != FocusResult::OK) {
+ ALOGW("setFocusedWindow on display %" PRId32 " ignored, reason:%s", displayId,
+ typeToString(result));
+ }
+ } // release lock
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
+InputDispatcher::FocusResult InputDispatcher::handleFocusRequestLocked(
+ const FocusRequest& request) {
+ const int32_t displayId = request.displayId;
+ const sp<IBinder> newFocusedToken = request.token;
+ const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+
+ if (oldFocusedToken == request.token) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow on display %" PRId32 " ignored, reason: already focused",
+ displayId);
+ return FocusResult::OK;
+ }
+
+ FocusResult result = checkTokenFocusableLocked(newFocusedToken, displayId);
+ if (result != FocusResult::OK) {
+ return result;
+ }
+
+ std::string_view reason =
+ (request.focusedToken) ? "setFocusedWindow_FocusCheck" : "setFocusedWindow";
+ onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, reason);
+ return FocusResult::OK;
+}
+
+void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
+ const sp<IBinder>& newFocusedToken, int32_t displayId,
+ std::string_view reason) {
+ if (oldFocusedToken) {
+ std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken);
if (focusedInputChannel) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
- enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/, reason);
+ enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason);
}
- mFocusedWindowHandlesByDisplay.erase(displayId);
+ mFocusedWindowTokenByDisplay.erase(displayId);
}
- if (newFocusedWindowHandle) {
- if (DEBUG_FOCUS) {
- ALOGD("Focus entered window: %s in display %" PRId32,
- newFocusedWindowHandle->getName().c_str(), displayId);
- }
- mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
- enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/, reason);
+ if (newFocusedToken) {
+ mFocusedWindowTokenByDisplay[displayId] = newFocusedToken;
+ enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
}
if (mFocusedDisplayId == displayId) {
- notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+ notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
}
}
+
+/**
+ * Checks if the window token can be focused on a display. The token can be focused if there is
+ * at least one window handle that is visible with the same token and all window handles with the
+ * same token are focusable.
+ *
+ * In the case of mirroring, two windows may share the same window token and their visibility
+ * might be different. Example, the mirrored window can cover the window its mirroring. However,
+ * we expect the focusability of the windows to match since its hard to reason why one window can
+ * receive focus events and the other cannot when both are backed by the same input channel.
+ */
+InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp<IBinder>& token,
+ int32_t displayId) const {
+ bool allWindowsAreFocusable = true;
+ bool visibleWindowFound = false;
+ bool windowFound = false;
+ for (const sp<InputWindowHandle>& window : getWindowHandlesLocked(displayId)) {
+ if (window->getToken() != token) {
+ continue;
+ }
+ windowFound = true;
+ if (window->getInfo()->visible) {
+ // Check if at least a single window is visible.
+ visibleWindowFound = true;
+ }
+ if (!window->getInfo()->focusable) {
+ // Check if all windows with the window token are focusable.
+ allWindowsAreFocusable = false;
+ break;
+ }
+ }
+
+ if (!windowFound) {
+ return FocusResult::NO_WINDOW;
+ }
+ if (!allWindowsAreFocusable) {
+ return FocusResult::NOT_FOCUSABLE;
+ }
+ if (!visibleWindowFound) {
+ return FocusResult::NOT_VISIBLE;
+ }
+
+ return FocusResult::OK;
+}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8988714..235a8d3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -31,6 +31,7 @@
#include "TouchState.h"
#include "TouchedWindow.h"
+#include <attestation/HmacKeyManager.h>
#include <input/Input.h>
#include <input/InputApplication.h>
#include <input/InputTransport.h>
@@ -58,16 +59,6 @@
class Connection;
-class HmacKeyManager {
-public:
- HmacKeyManager();
- std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
-
-private:
- std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
- const std::array<uint8_t, 128> mHmacKey;
-};
-
/* Dispatches events to input targets. Some functions of the input dispatcher, such as
* identifying input targets, are controlled by a separate policy object.
*
@@ -104,10 +95,10 @@
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode,
- std::chrono::milliseconds timeout,
- uint32_t policyFlags) override;
+ virtual android::os::InputEventInjectionResult injectInputEvent(
+ const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
+ uint32_t policyFlags) override;
virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
@@ -121,18 +112,22 @@
virtual void setInputDispatchMode(bool enabled, bool frozen) override;
virtual void setInputFilterEnabled(bool enabled) override;
virtual void setInTouchMode(bool inTouchMode) override;
+ virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
+ virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
- virtual status_t registerInputChannel(
- const std::shared_ptr<InputChannel>& inputChannel) override;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ const std::string& name) override;
virtual void setFocusedWindow(const FocusRequest&) override;
- virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) override;
- virtual status_t unregisterInputChannel(const InputChannel& inputChannel) override;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+ int32_t displayId, bool isGestureMonitor, const std::string& name) override;
+ virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
virtual status_t pilferPointers(const sp<IBinder>& token) override;
+ std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
+
private:
enum class DropReason {
NOT_DROPPED,
@@ -143,6 +138,14 @@
STALE,
};
+ enum class FocusResult {
+ OK,
+ NO_WINDOW,
+ NOT_FOCUSABLE,
+ NOT_VISIBLE,
+ };
+ static const char* typeToString(FocusResult result);
+
std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
@@ -178,7 +181,7 @@
void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
// Enqueues a focus event.
- void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus,
+ void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
std::string_view reason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
@@ -239,7 +242,8 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResult(EventEntry* entry, int32_t injectionResult);
+ void setInjectionResult(EventEntry* entry,
+ android::os::InputEventInjectionResult injectionResult);
std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(EventEntry* entry);
@@ -295,20 +299,31 @@
bool mDispatchFrozen GUARDED_BY(mLock);
bool mInputFilterEnabled GUARDED_BY(mLock);
bool mInTouchMode GUARDED_BY(mLock);
+ float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
+ android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
GUARDED_BY(mLock);
void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
int32_t displayId) REQUIRES(mLock);
- // Get window handles by display, return an empty vector if not found.
- std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+ // Get a reference to window handles by display, return an empty vector if not found.
+ const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const
REQUIRES(mLock);
sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
REQUIRES(mLock);
+
+ // Same function as above, but faster. Since displayId is provided, this avoids the need
+ // to loop through all displays.
+ sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+ int displayId) const REQUIRES(mLock);
std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
REQUIRES(mLock);
+ sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
+ FocusResult handleFocusRequestLocked(const FocusRequest&) REQUIRES(mLock);
+ FocusResult checkTokenFocusableLocked(const sp<IBinder>& token, int32_t displayId) const
+ REQUIRES(mLock);
/*
* Validate and update InputWindowHandles for a given display.
@@ -317,9 +332,11 @@
const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
REQUIRES(mLock);
- // Focus tracking for keys, trackball, etc.
- std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
- GUARDED_BY(mLock);
+ // Focus tracking for keys, trackball, etc. A window token can be associated with one or more
+ // InputWindowHandles. If a window is mirrored, the window and its mirror will share the same
+ // token. Focus is tracked by the token per display and the events are dispatched to the
+ // channel associated by this token.
+ std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock);
std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
@@ -378,6 +395,19 @@
* Used to raise an ANR when we have no focused window.
*/
std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+ /**
+ * The displayId that the focused application is associated with.
+ */
+ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+ void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
+
+ /**
+ * This map will store the pending focus requests that cannot be currently processed. This can
+ * happen if the window requested to be focused is not currently visible. Such a window might
+ * become visible later, and these requests would be processed at that time.
+ */
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests
+ GUARDED_BY(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -402,13 +432,12 @@
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
int32_t getTargetDisplayId(const EventEntry& entry);
- int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry,
- std::vector<InputTarget>& inputTargets,
- nsecs_t* nextWakeupTime,
- bool* outConflictingPointerActions) REQUIRES(mLock);
+ 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);
std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
REQUIRES(mLock);
@@ -426,6 +455,17 @@
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
+
+ struct TouchOcclusionInfo {
+ bool hasBlockingOcclusion;
+ float obscuringOpacity;
+ std::string obscuringPackage;
+ int32_t obscuringUid;
+ };
+
+ TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
+ int32_t x, int32_t y) const REQUIRES(mLock);
+ bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
int32_t y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
@@ -486,13 +526,14 @@
void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
+ std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
// Registration.
- void removeMonitorChannelLocked(const InputChannel& inputChannel) REQUIRES(mLock);
+ void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
void removeMonitorChannelLocked(
- const InputChannel& inputChannel,
+ const sp<IBinder>& connectionToken,
std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock);
- status_t unregisterInputChannelLocked(const InputChannel& inputChannel, bool notify)
+ status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -500,13 +541,13 @@
uint32_t seq, bool handled) REQUIRES(mLock);
void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
- void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus, int32_t displayId,
- std::string_view reason) REQUIRES(mLock);
- void notifyFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
- const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
- void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus,
+ int32_t displayId, std::string_view reason) REQUIRES(mLock);
+ void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+ REQUIRES(mLock);
+ void onAnrLocked(const Connection& connection) REQUIRES(mLock);
void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
+ void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
REQUIRES(mLock);
void updateLastAnrStateLocked(const std::shared_ptr<InputApplicationHandle>& application,
@@ -520,6 +561,7 @@
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+ void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
REQUIRES(mLock);
void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 179a263..9154d48 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,36 +18,20 @@
#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
#include <InputListener.h>
+#include <android-base/result.h>
#include <android/FocusRequest.h>
+#include <android/os/BlockUntrustedTouchesMode.h>
#include <android/os/ISetInputWindowsListener.h>
+#include <android/os/InputEventInjectionResult.h>
+#include <android/os/InputEventInjectionSync.h>
#include <input/InputApplication.h>
#include <input/InputTransport.h>
#include <input/InputWindow.h>
#include <unordered_map>
+
namespace android {
-/*
- * Constants used to report the outcome of input event injection.
- */
-enum {
- /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
- INPUT_EVENT_INJECTION_PENDING = -1,
-
- /* Injection succeeded. */
- INPUT_EVENT_INJECTION_SUCCEEDED = 0,
-
- /* Injection failed because the injector did not have permission to inject
- * into the application with input focus. */
- INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
-
- /* Injection failed because there were no available input targets. */
- INPUT_EVENT_INJECTION_FAILED = 2,
-
- /* Injection failed due to a timeout. */
- INPUT_EVENT_INJECTION_TIMED_OUT = 3
-};
-
/* Notifies the system about input events generated by the input reader.
* The dispatcher is expected to be mostly asynchronous. */
class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
@@ -89,9 +73,10 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
- int32_t injectorUid, int32_t syncMode,
- std::chrono::milliseconds timeout, uint32_t policyFlags) = 0;
+ virtual android::os::InputEventInjectionResult injectInputEvent(
+ const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+ android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
+ uint32_t policyFlags) = 0;
/*
* Check whether InputEvent actually happened by checking the signature of the event.
@@ -144,6 +129,21 @@
*/
virtual void setInTouchMode(bool inTouchMode) = 0;
+ /**
+ * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+ * For certain window types (eg. SAWs), the decision of honoring
+ * FLAG_NOT_TOUCHABLE or not depends on the combined obscuring opacity of
+ * the windows above the touch-consuming window.
+ */
+ virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0;
+
+ /**
+ * Sets the mode of the block untrusted touches feature.
+ *
+ * TODO(b/169067926): Clean-up feature modes.
+ */
+ virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) = 0;
+
/* Transfers touch focus from one window to another window.
*
* Returns true on success. False if the window did not actually have touch focus.
@@ -155,13 +155,16 @@
*/
virtual void setFocusedWindow(const FocusRequest&) = 0;
- /* Registers input channels that may be used as targets for input events.
+ /**
+ * Creates an input channel that may be used as targets for input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+ const std::string& name) = 0;
- /* Registers input channels to be used to monitor input events.
+ /**
+ * Creates an input channel to be used to monitor input events.
*
* Each monitor must target a specific display and will only receive input events sent to that
* display. If the monitor is a gesture monitor, it will only receive pointer events on the
@@ -169,14 +172,14 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool gestureMonitor) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+ int32_t displayId, bool gestureMonitor, const std::string& name) = 0;
- /* Unregister input channels that will no longer receive input events.
+ /* Removes input channels that will no longer receive input events.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t unregisterInputChannel(const InputChannel& inputChannel) = 0;
+ virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) = 0;
/* Allows an input monitor steal the current pointer stream away from normal input windows.
*
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index d04d797..463c5f1 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -55,6 +55,9 @@
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
+ /* Notifies the system that an untrusted touch occurred. */
+ virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0;
+
/* Gets the input dispatcher configuration. */
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 3496a24..9e797e4 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -23,6 +23,7 @@
header_libs: ["jni_headers"],
shared_libs: [
+ "libbase",
"libbinder",
"libcrypto",
"libcutils",
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index ff69800..2ebdbcf 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -29,14 +29,14 @@
#include <hardware/input.h>
#include <input/InputDevice.h>
+#include <input/PropertyMap.h>
#include <utils/Log.h>
-#include <utils/PropertyMap.h>
#include <utils/String8.h>
#define INDENT2 " "
struct input_property_map {
- android::PropertyMap* propertyMap;
+ std::unique_ptr<android::PropertyMap> propertyMap;
};
struct input_property {
@@ -225,16 +225,17 @@
ALOGD("No input device configuration file found for device '%s'.",
idi.name.c_str());
} else {
- auto propMap = new input_property_map_t();
- status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap);
- if (status) {
+ std::unique_ptr<input_property_map_t> propMap = std::make_unique<input_property_map_t>();
+ android::base::Result<std::unique_ptr<PropertyMap>> result =
+ PropertyMap::load(configFile.c_str());
+ if (!result.ok()) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
idi.name.c_str());
- delete propMap;
return nullptr;
}
- return propMap;
+ propMap->propertyMap = std::move(*result);
+ return propMap.release();
}
return nullptr;
}
@@ -278,7 +279,6 @@
void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) {
if (map != nullptr) {
- delete map->propertyMap;
delete map;
}
}
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 2a2cea5..47773d9 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -48,8 +48,10 @@
const sp<ISetInputWindowsListener>&) {
return binder::Status::ok();
}
- binder::Status registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
- binder::Status unregisterInputChannel(const InputChannel&) { return binder::Status::ok(); }
+ binder::Status createInputChannel(const std::string&, InputChannel*) {
+ return binder::Status::ok();
+ }
+ binder::Status removeInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); }
private:
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index a24b293..ffd8bf2 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -78,7 +78,7 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) = 0;
+ virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
/* Query current input state. */
virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
@@ -341,7 +341,7 @@
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
/* Gets the keyboard layout for a particular input device. */
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier) = 0;
/* Gets a user-supplied alias for a particular input device, or an empty string if none. */
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 76b9419..c5210b5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -216,10 +216,7 @@
return !isVirtual && enabled;
}
-const sp<KeyCharacterMap>& EventHub::Device::getKeyCharacterMap() const {
- if (combinedKeyMap != nullptr) {
- return combinedKeyMap;
- }
+const std::shared_ptr<KeyCharacterMap> EventHub::Device::getKeyCharacterMap() const {
return keyMap.keyCharacterMap;
}
@@ -283,14 +280,14 @@
if (configurationFile.empty()) {
ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
} else {
- PropertyMap* propertyMap;
- status_t status = PropertyMap::load(String8(configurationFile.c_str()), &propertyMap);
- if (status) {
+ android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(configurationFile.c_str());
+ if (!propertyMap.ok()) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
identifier.name.c_str());
} else {
- configuration = std::unique_ptr<PropertyMap>(propertyMap);
+ configuration = std::move(*propertyMap);
}
}
}
@@ -661,8 +658,8 @@
if (device != nullptr) {
// Check the key character map first.
- sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
- if (kcm != nullptr) {
+ const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+ if (kcm) {
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
*outFlags = 0;
status = NO_ERROR;
@@ -677,7 +674,7 @@
}
if (status == NO_ERROR) {
- if (kcm != nullptr) {
+ if (kcm) {
kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
} else {
*outMetaState = metaState;
@@ -754,7 +751,7 @@
}
}
-sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
+const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device != nullptr) {
@@ -763,15 +760,13 @@
return nullptr;
}
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
- if (device != nullptr) {
- if (map != device->overlayKeyMap) {
- device->overlayKeyMap = map;
- device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
- return true;
- }
+ if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) {
+ device->keyMap.keyCharacterMap->combine(*map);
+ device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName();
+ return true;
}
return false;
}
@@ -979,18 +974,30 @@
mNeedToSendFinishedDeviceScan = true;
}
- for (auto it = mOpeningDevices.begin(); it != mOpeningDevices.end();) {
- std::unique_ptr<Device> device = std::move(*it);
+ while (!mOpeningDevices.empty()) {
+ std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
+ mOpeningDevices.pop_back();
ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
- auto [dev_it, insert] = mDevices.insert_or_assign(device->id, std::move(device));
- if (!insert) {
+
+ // Try to find a matching video device by comparing device names
+ for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
+ it++) {
+ std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
+ if (tryAddVideoDevice(*device, videoDevice)) {
+ // videoDevice was transferred to 'device'
+ it = mUnattachedVideoDevices.erase(it);
+ break;
+ }
+ }
+
+ auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
+ if (!inserted) {
ALOGW("Device id %d exists, replaced.", device->id);
}
- it = mOpeningDevices.erase(it);
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
@@ -1536,21 +1543,6 @@
device->setLedForControllerLocked();
}
- // Find a matching video device by comparing device names
- // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll
- for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
- if (device->identifier.name == videoDevice->getName()) {
- device->videoDevice = std::move(videoDevice);
- break;
- }
- }
- mUnattachedVideoDevices
- .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
- [](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
- return videoDevice == nullptr;
- }),
- mUnattachedVideoDevices.end());
-
if (registerDeviceForEpollLocked(*device) != OK) {
return -1;
}
@@ -1576,12 +1568,8 @@
}
// Transfer ownership of this video device to a matching input device
for (const auto& [id, device] : mDevices) {
- if (videoDevice->getName() == device->identifier.name) {
- device->videoDevice = std::move(videoDevice);
- if (device->enabled) {
- registerVideoDeviceForEpollLocked(*device->videoDevice);
- }
- return;
+ if (tryAddVideoDevice(*device, videoDevice)) {
+ return; // 'device' now owns 'videoDevice'
}
}
@@ -1592,6 +1580,18 @@
mUnattachedVideoDevices.push_back(std::move(videoDevice));
}
+bool EventHub::tryAddVideoDevice(EventHub::Device& device,
+ std::unique_ptr<TouchVideoDevice>& videoDevice) {
+ if (videoDevice->getName() != device.identifier.name) {
+ return false;
+ }
+ device.videoDevice = std::move(videoDevice);
+ if (device.enabled) {
+ registerVideoDeviceForEpollLocked(*device.videoDevice);
+ }
+ return true;
+}
+
bool EventHub::isDeviceEnabled(int32_t deviceId) {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
@@ -1848,8 +1848,6 @@
device->keyMap.keyCharacterMapFile.c_str());
dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
device->configurationFile.c_str());
- dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
- toString(device->overlayKeyMap != nullptr));
dump += INDENT3 "VideoDevice: ";
if (device->videoDevice) {
dump += device->videoDevice->dump() + "\n";
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 14f922f..271bc2f 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -85,12 +85,13 @@
bumpGeneration();
}
-void InputDevice::dump(std::string& dump) {
+void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
InputDeviceInfo deviceInfo;
getDeviceInfo(&deviceInfo);
dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(),
deviceInfo.getDisplayName().c_str());
+ dump += StringPrintf(INDENT "%s", eventHubDevStr.c_str());
dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
@@ -102,13 +103,14 @@
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+ dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber());
const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
if (!ranges.empty()) {
dump += INDENT2 "Motion Ranges:\n";
for (size_t i = 0; i < ranges.size(); i++) {
const InputDeviceInfo::MotionRange& range = ranges[i];
- const char* label = getAxisLabel(range.axis);
+ const char* label = InputEventLookup::getAxisLabel(range.axis);
char name[32];
if (label) {
strncpy(name, label, sizeof(name));
@@ -201,6 +203,8 @@
// insert the context into the devices set
mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+ // Must change generation to flag this device as changed
+ bumpGeneration();
}
void InputDevice::removeEventHubDevice(int32_t eventHubId) {
@@ -240,7 +244,7 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
- sp<KeyCharacterMap> keyboardLayout =
+ std::shared_ptr<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
bool shouldBumpGeneration = false;
for_each_subdevice(
@@ -481,6 +485,10 @@
return count;
}
+void InputDevice::updateLedState(bool reset) {
+ for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
+}
+
InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
: mDevice(device),
mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index bb1ccbf..2028b91 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -47,6 +47,7 @@
mEventHub(eventHub),
mPolicy(policy),
mGlobalMetaState(0),
+ mLedMetaState(AMETA_NUM_LOCK_ON),
mGeneration(1),
mNextInputDeviceId(END_RESERVED_ID),
mDisableVirtualKeysTimeout(LLONG_MIN),
@@ -86,7 +87,6 @@
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
- std::vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
@@ -127,13 +127,12 @@
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
- getInputDevicesLocked(inputDevices);
}
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
- mPolicy->notifyInputDevicesChanged(inputDevices);
+ mPolicy->notifyInputDevicesChanged(getInputDevicesLocked());
}
// Flush queued events out to the listener.
@@ -206,6 +205,14 @@
}
mDevices.emplace(eventHubId, device);
+ // Add device to device to EventHub ids map.
+ const auto mapIt = mDeviceToEventHubIdsMap.find(device);
+ if (mapIt == mDeviceToEventHubIdsMap.end()) {
+ std::vector<int32_t> ids = {eventHubId};
+ mDeviceToEventHubIdsMap.emplace(device, ids);
+ } else {
+ mapIt->second.push_back(eventHubId);
+ }
bumpGenerationLocked();
if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
@@ -222,6 +229,17 @@
std::shared_ptr<InputDevice> device = std::move(deviceIt->second);
mDevices.erase(deviceIt);
+ // Erase device from device to EventHub ids map.
+ auto mapIt = mDeviceToEventHubIdsMap.find(device);
+ if (mapIt != mDeviceToEventHubIdsMap.end()) {
+ std::vector<int32_t>& eventHubIds = mapIt->second;
+ eventHubIds.erase(std::remove_if(eventHubIds.begin(), eventHubIds.end(),
+ [eventHubId](int32_t eId) { return eId == eventHubId; }),
+ eventHubIds.end());
+ if (eventHubIds.size() == 0) {
+ mDeviceToEventHubIdsMap.erase(mapIt);
+ }
+ }
bumpGenerationLocked();
if (device->isIgnored()) {
@@ -353,6 +371,18 @@
return mGlobalMetaState;
}
+void InputReader::updateLedMetaStateLocked(int32_t metaState) {
+ mLedMetaState = metaState;
+ for (auto& devicePair : mDevices) {
+ std::shared_ptr<InputDevice>& device = devicePair.second;
+ device->updateLedState(false);
+ }
+}
+
+int32_t InputReader::getLedMetaStateLocked() {
+ return mLedMetaState;
+}
+
void InputReader::notifyExternalStylusPresenceChanged() {
refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
}
@@ -442,22 +472,23 @@
return ++mGeneration;
}
-void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) {
+std::vector<InputDeviceInfo> InputReader::getInputDevices() const {
AutoMutex _l(mLock);
- getInputDevicesLocked(outInputDevices);
+ return getInputDevicesLocked();
}
-void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
- outInputDevices.clear();
+std::vector<InputDeviceInfo> InputReader::getInputDevicesLocked() const {
+ std::vector<InputDeviceInfo> outInputDevices;
+ outInputDevices.reserve(mDeviceToEventHubIdsMap.size());
- for (auto& devicePair : mDevices) {
- std::shared_ptr<InputDevice>& device = devicePair.second;
+ for (const auto& [device, eventHubIds] : mDeviceToEventHubIdsMap) {
if (!device->isIgnored()) {
InputDeviceInfo info;
device->getDeviceInfo(&info);
outInputDevices.push_back(info);
}
}
+ return outInputDevices;
}
int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) {
@@ -622,11 +653,17 @@
mEventHub->dump(dump);
dump += "\n";
- dump += "Input Reader State:\n";
+ dump += StringPrintf("Input Reader State (Nums of device: %zu):\n",
+ mDeviceToEventHubIdsMap.size());
- for (const auto& devicePair : mDevices) {
- const std::shared_ptr<InputDevice>& device = devicePair.second;
- device->dump(dump);
+ for (const auto& devicePair : mDeviceToEventHubIdsMap) {
+ const std::shared_ptr<InputDevice>& device = devicePair.first;
+ std::string eventHubDevStr = INDENT "EventHub Devices: [ ";
+ for (const auto& eId : devicePair.second) {
+ eventHubDevStr += StringPrintf("%d ", eId);
+ }
+ eventHubDevStr += "] \n";
+ device->dump(dump, eventHubDevStr);
}
dump += INDENT "Configuration:\n";
@@ -710,6 +747,16 @@
return mReader->getGlobalMetaStateLocked();
}
+void InputReader::ContextImpl::updateLedMetaState(int32_t metaState) {
+ // lock is already held by the input loop
+ mReader->updateLedMetaStateLocked(metaState);
+}
+
+int32_t InputReader::ContextImpl::getLedMetaState() {
+ // lock is already held by the input loop
+ return mReader->getLedMetaStateLocked();
+}
+
void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
// lock is already held by the input loop
mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index ff12d98..edb82d3 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -28,6 +28,7 @@
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
+#include <input/PropertyMap.h>
#include <input/VirtualKeyMap.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -37,7 +38,6 @@
#include <utils/List.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
-#include <utils/PropertyMap.h>
#include "TouchVideoDevice.h"
#include "VibrationElement.h"
@@ -226,8 +226,9 @@
virtual void getVirtualKeyDefinitions(
int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
- virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
+ virtual const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+ virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+ std::shared_ptr<KeyCharacterMap> map) = 0;
/* Control the vibrator. */
virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
@@ -373,8 +374,10 @@
int32_t deviceId,
std::vector<VirtualKeyDefinition>& outVirtualKeys) const override final;
- sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override final;
- bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) override final;
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(
+ int32_t deviceId) const override final;
+ bool setKeyboardLayoutOverlay(int32_t deviceId,
+ std::shared_ptr<KeyCharacterMap> map) override final;
void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
void cancelVibrate(int32_t deviceId) override final;
@@ -421,9 +424,6 @@
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
- sp<KeyCharacterMap> overlayKeyMap;
- sp<KeyCharacterMap> combinedKeyMap;
-
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
@@ -441,7 +441,7 @@
bool hasValidFd() const;
const bool isVirtual; // set if fd < 0 is passed to constructor
- const sp<KeyCharacterMap>& getKeyCharacterMap() const;
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const;
template <std::size_t N>
status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
@@ -460,6 +460,14 @@
status_t openDeviceLocked(const std::string& devicePath);
void openVideoDeviceLocked(const std::string& devicePath);
+ /**
+ * Try to associate a video device with an input device. If the association succeeds,
+ * the videoDevice is moved into the input device. 'videoDevice' will become null if this
+ * happens.
+ * Return true if the association succeeds.
+ * Return false otherwise.
+ */
+ bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
void createVirtualKeyboardLocked();
void addDeviceLocked(std::unique_ptr<Device> device);
void assignDescriptorLocked(InputDeviceIdentifier& identifier);
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index da36a48..7d160eb 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -20,8 +20,8 @@
#include <input/DisplayViewport.h>
#include <input/Flags.h>
#include <input/InputDevice.h>
+#include <input/PropertyMap.h>
#include <stdint.h>
-#include <utils/PropertyMap.h>
#include <optional>
#include <unordered_map>
@@ -67,7 +67,7 @@
bool isEnabled();
void setEnabled(bool enabled, nsecs_t when);
- void dump(std::string& dump);
+ 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);
@@ -98,6 +98,8 @@
std::optional<int32_t> getAssociatedDisplayId();
+ void updateLedState(bool reset);
+
size_t getMapperCount();
// construct and add a mapper to the input device
@@ -259,10 +261,10 @@
inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys);
}
- inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+ inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
return mEventHub->getKeyCharacterMap(mId);
}
- inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
+ inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) {
return mEventHub->setKeyboardLayoutOverlay(mId, map);
}
inline void vibrate(const VibrationElement& element) {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9cb2052..563018a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -55,34 +55,32 @@
const sp<InputListenerInterface>& listener);
virtual ~InputReader();
- virtual void dump(std::string& dump) override;
- virtual void monitor() override;
+ void dump(std::string& dump) override;
+ void monitor() override;
- virtual status_t start() override;
- virtual status_t stop() override;
+ status_t start() override;
+ status_t stop() override;
- virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
+ std::vector<InputDeviceInfo> getInputDevices() const override;
- virtual bool isInputDeviceEnabled(int32_t deviceId) override;
+ bool isInputDeviceEnabled(int32_t deviceId) override;
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) override;
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) override;
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+ int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) override;
+ 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;
- virtual void toggleCapsLockState(int32_t deviceId) override;
+ void toggleCapsLockState(int32_t deviceId) override;
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) override;
+ bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override;
- virtual void requestRefreshConfiguration(uint32_t changes) override;
+ void requestRefreshConfiguration(uint32_t changes) override;
- virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
- ssize_t repeat, int32_t token) override;
- virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
+ void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat,
+ int32_t token) override;
+ void cancelVibrate(int32_t deviceId, int32_t token) override;
- virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+ bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
protected:
// These members are protected so they can be instrumented by test cases.
@@ -100,21 +98,22 @@
public:
explicit ContextImpl(InputReader* reader);
- virtual void updateGlobalMetaState() override;
- virtual int32_t getGlobalMetaState() override;
- virtual void disableVirtualKeysUntil(nsecs_t time) override;
- virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
- virtual void fadePointer() override;
- virtual std::shared_ptr<PointerControllerInterface> getPointerController(
- int32_t deviceId) override;
- virtual void requestTimeoutAtTime(nsecs_t when) override;
- virtual int32_t bumpGeneration() override;
- virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
- virtual void dispatchExternalStylusState(const StylusState& outState) override;
- virtual InputReaderPolicyInterface* getPolicy() override;
- virtual InputListenerInterface* getListener() override;
- virtual EventHubInterface* getEventHub() override;
- virtual int32_t getNextId() override;
+ void updateGlobalMetaState() override;
+ int32_t getGlobalMetaState() override;
+ void disableVirtualKeysUntil(nsecs_t time) override;
+ bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
+ void fadePointer() override;
+ std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override;
+ void requestTimeoutAtTime(nsecs_t when) override;
+ int32_t bumpGeneration() override;
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+ void dispatchExternalStylusState(const StylusState& outState) override;
+ InputReaderPolicyInterface* getPolicy() override;
+ InputListenerInterface* getListener() override;
+ EventHubInterface* getEventHub() override;
+ int32_t getNextId() override;
+ void updateLedMetaState(int32_t metaState) override;
+ int32_t getLedMetaState() override;
} mContext;
friend class ContextImpl;
@@ -122,7 +121,7 @@
private:
std::unique_ptr<InputThread> mThread;
- Mutex mLock;
+ mutable Mutex mLock;
Condition mReaderIsAliveCondition;
@@ -143,6 +142,11 @@
// to lookup the input device instance from the EventHub device id.
std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices;
+ // An input device contains one or more eventHubId, this map provides a way to lookup the
+ // EventHubIds contained in the input device from the input device instance.
+ std::unordered_map<std::shared_ptr<InputDevice>, std::vector<int32_t> /*eventHubId*/>
+ mDeviceToEventHubIdsMap;
+
// low-level input event decoding and device management
void processEventsLocked(const RawEvent* rawEvents, size_t count);
@@ -157,6 +161,10 @@
void updateGlobalMetaStateLocked();
int32_t getGlobalMetaStateLocked();
+ int32_t mLedMetaState;
+ void updateLedMetaStateLocked(int32_t metaState);
+ int32_t getLedMetaStateLocked();
+
void notifyExternalStylusPresenceChanged();
void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
void dispatchExternalStylusState(const StylusState& state);
@@ -173,7 +181,7 @@
int32_t mNextInputDeviceId;
int32_t nextInputDeviceIdLocked();
- void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
+ std::vector<InputDeviceInfo> getInputDevicesLocked() const;
nsecs_t mDisableVirtualKeysTimeout;
void disableVirtualKeysUntilLocked(nsecs_t time);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index ffb8d8c..dc807f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -59,6 +59,9 @@
virtual EventHubInterface* getEventHub() = 0;
virtual int32_t getNextId() = 0;
+
+ virtual void updateLedMetaState(int32_t metaState) = 0;
+ virtual int32_t getLedMetaState() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index d9fc5cc..56ab928 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -74,6 +74,7 @@
virtual void updateExternalStylusState(const StylusState& state);
virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+ virtual void updateLedState(bool reset) {}
protected:
InputDeviceContext& mDeviceContext;
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 57acba5..abd8aa9 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -73,14 +73,14 @@
dump += INDENT3 "Axes:\n";
for (const auto& [rawAxis, axis] : mAxes) {
- const char* label = getAxisLabel(axis.axisInfo.axis);
+ const char* label = InputEventLookup::getAxisLabel(axis.axisInfo.axis);
if (label) {
dump += StringPrintf(INDENT4 "%s", label);
} else {
dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis);
}
if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
- label = getAxisLabel(axis.axisInfo.highAxis);
+ label = InputEventLookup::getAxisLabel(axis.axisInfo.highAxis);
if (label) {
dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue);
} else {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index bd4232d..8b9f235 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -390,11 +390,14 @@
bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
int32_t oldMetaState = mMetaState;
int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
- bool metaStateChanged = oldMetaState != newMetaState;
+ int32_t metaStateChanged = oldMetaState ^ newMetaState;
if (metaStateChanged) {
mMetaState = newMetaState;
- updateLedState(false);
-
+ constexpr int32_t allLedMetaState =
+ AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON;
+ if ((metaStateChanged & allLedMetaState) != 0) {
+ getContext()->updateLedMetaState(newMetaState & allLedMetaState);
+ }
getContext()->updateGlobalMetaState();
}
@@ -415,6 +418,26 @@
}
void KeyboardInputMapper::updateLedState(bool reset) {
+ mMetaState |= getContext()->getLedMetaState();
+
+ constexpr int32_t META_NUM = 3;
+ const std::array<int32_t, META_NUM> keyCodes = {AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
+ AKEYCODE_SCROLL_LOCK};
+ const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
+ AMETA_SCROLL_LOCK_ON};
+ std::array<uint8_t, META_NUM> flags = {0, 0, 0};
+ bool hasKeyLayout =
+ getDeviceContext().markSupportedKeyCodes(META_NUM, keyCodes.data(), flags.data());
+ // If the device doesn't have the physical meta key it shouldn't generate the corresponding
+ // meta state.
+ if (hasKeyLayout) {
+ for (int i = 0; i < META_NUM; i++) {
+ if (!flags[i]) {
+ mMetaState &= ~metaCodes[i];
+ }
+ }
+ }
+
updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset);
updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset);
updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 0bdeded..4c0b42a 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -42,6 +42,7 @@
virtual int32_t getMetaState() override;
virtual void updateMetaState(int32_t keyCode) override;
virtual std::optional<int32_t> getAssociatedDisplayId() override;
+ virtual void updateLedState(bool reset);
private:
// The current viewport.
@@ -93,7 +94,6 @@
void resetLedState();
void initializeLedState(LedState& ledState, int32_t led);
- void updateLedState(bool reset);
void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
std::optional<DisplayViewport> findViewport(nsecs_t when,
const InputReaderConfiguration* config);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index e867c6f..ea84835 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -18,6 +18,7 @@
#include "../Macros.h"
// clang-format on
+#include <input/NamedEnum.h>
#include "TouchInputMapper.h"
#include "CursorButtonAccumulator.h"
@@ -192,6 +193,20 @@
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.haveSize) {
info->addMotionRange(mOrientedRanges.size);
}
@@ -243,7 +258,8 @@
}
void TouchInputMapper::dump(std::string& dump) {
- dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
+ dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n",
+ NamedEnum::string(mDeviceMode).c_str());
dumpParameters(dump);
dumpVirtualKeys(dump);
dumpRawPointerAxes(dump);
@@ -331,22 +347,6 @@
}
}
-const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
- switch (deviceMode) {
- case DeviceMode::DISABLED:
- return "disabled";
- case DeviceMode::DIRECT:
- return "direct";
- case DeviceMode::UNSCALED:
- return "unscaled";
- case DeviceMode::NAVIGATION:
- return "navigation";
- case DeviceMode::POINTER:
- return "pointer";
- }
- return "unknown";
-}
-
void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
uint32_t changes) {
InputMapper::configure(when, config, changes);
@@ -498,33 +498,9 @@
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
- switch (mParameters.gestureMode) {
- case Parameters::GestureMode::SINGLE_TOUCH:
- dump += INDENT4 "GestureMode: single-touch\n";
- break;
- case Parameters::GestureMode::MULTI_TOUCH:
- dump += INDENT4 "GestureMode: multi-touch\n";
- break;
- default:
- assert(false);
- }
+ dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n";
- switch (mParameters.deviceType) {
- case Parameters::DeviceType::TOUCH_SCREEN:
- dump += INDENT4 "DeviceType: touchScreen\n";
- break;
- case Parameters::DeviceType::TOUCH_PAD:
- dump += INDENT4 "DeviceType: touchPad\n";
- break;
- case Parameters::DeviceType::TOUCH_NAVIGATION:
- dump += INDENT4 "DeviceType: touchNavigation\n";
- break;
- case Parameters::DeviceType::POINTER:
- dump += INDENT4 "DeviceType: pointer\n";
- break;
- default:
- ALOG_ASSERT(false);
- }
+ dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n";
dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
"displayId='%s'\n",
@@ -2610,14 +2586,14 @@
// Update the velocity tracker.
{
- VelocityTracker::Position positions[MAX_POINTERS];
- uint32_t count = 0;
- for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
+ std::vector<VelocityTracker::Position> positions;
+ for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
mCurrentRawState.rawPointerData.pointerForId(id);
- positions[count].x = pointer.x * mPointerXMovementScale;
- positions[count].y = pointer.y * mPointerYMovementScale;
+ float x = pointer.x * mPointerXMovementScale;
+ float y = pointer.y * mPointerYMovementScale;
+ positions.push_back({x, y});
}
mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
positions);
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
index b5c5c9e..5c8a8da 100644
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -26,4 +26,5 @@
void getInputWindows(out InputWindowInfo[] inputHandles);
void getInputChannels(out InputChannel[] channels);
void getLastFocusRequest(out FocusRequest request);
+ void resetInputManager();
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index da50af5..dc32003 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -29,6 +29,8 @@
#include <vector>
using android::base::StringPrintf;
+using android::os::InputEventInjectionResult;
+using android::os::InputEventInjectionSync;
using namespace android::flag_operators;
namespace android::inputdispatcher {
@@ -152,6 +154,7 @@
const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
ADD_FAILURE() << "Did not receive ANR callback";
+ return {};
}
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
@@ -214,6 +217,8 @@
virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ virtual void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
@@ -294,70 +299,6 @@
}
};
-// --- HmacKeyManagerTest ---
-
-class HmacKeyManagerTest : public testing::Test {
-protected:
- HmacKeyManager mHmacKeyManager;
-};
-
-/**
- * Ensure that separate calls to sign the same data are generating the same key.
- * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
- * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
- * tests.
- */
-TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
- KeyEvent event = getTestKeyEvent();
- VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
-
- std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
- std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
- ASSERT_EQ(hmac1, hmac2);
-}
-
-/**
- * Ensure that changes in VerifiedKeyEvent produce a different hmac.
- */
-TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
- KeyEvent event = getTestKeyEvent();
- VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
- std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
-
- verifiedEvent.deviceId += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.source += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.eventTimeNanos += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.displayId += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.action += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.downTimeNanos += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.flags += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.keyCode += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.scanCode += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.metaState += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-
- verifiedEvent.repeatCount += 1;
- ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
-}
-
// --- InputDispatcherTest ---
class InputDispatcherTest : public testing::Test {
@@ -392,6 +333,18 @@
ALOGE("%s", to.c_str());
}
}
+
+ void setFocusedWindow(const sp<InputWindowHandle>& window,
+ const sp<InputWindowHandle>& focusedWindow = nullptr) {
+ FocusRequest request;
+ request.token = window->getToken();
+ if (focusedWindow) {
+ request.focusedToken = focusedWindow->getToken();
+ }
+ request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ request.displayId = window->getInfo()->displayId;
+ mDispatcher->setFocusedWindow(request);
+ }
};
@@ -403,18 +356,18 @@
INVALID_HMAC,
/*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
ARBITRARY_TIME, ARBITRARY_TIME);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
@@ -441,9 +394,9 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
@@ -454,9 +407,9 @@
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -466,9 +419,9 @@
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
@@ -479,9 +432,9 @@
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -491,9 +444,9 @@
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties,
pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
@@ -502,9 +455,9 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -512,9 +465,9 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
@@ -524,9 +477,9 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerProperties[0].id = MAX_POINTER_ID + 1;
@@ -535,9 +488,9 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
@@ -548,9 +501,9 @@
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
- INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
+ InputEventInjectionSync::NONE, 0ms, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
@@ -600,10 +553,9 @@
class FakeInputReceiver {
public:
- explicit FakeInputReceiver(const std::shared_ptr<InputChannel>& clientChannel,
- const std::string name)
+ explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
: mName(name) {
- mConsumer = std::make_unique<InputConsumer>(clientChannel);
+ mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
}
InputEvent* consume() {
@@ -753,11 +705,10 @@
int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
: mName(name) {
if (token == std::nullopt) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
- token = serverChannel->getConnectionToken();
- dispatcher->registerInputChannel(std::move(serverChannel));
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher->createInputChannel(name);
+ token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
}
inputApplicationHandle->updateInfo();
@@ -789,6 +740,8 @@
void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+ void setVisible(bool visible) { mInfo.visible = visible; }
+
void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
mInfo.dispatchingTimeout = timeout;
}
@@ -917,10 +870,11 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
-static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
- int32_t displayId = ADISPLAY_ID_NONE,
- int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
- std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
+static InputEventInjectionResult injectKey(
+ const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ int32_t displayId = ADISPLAY_ID_NONE,
+ InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
KeyEvent event;
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -935,13 +889,13 @@
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
}
-static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
+ int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
}
@@ -1056,22 +1010,22 @@
std::vector<PointerBuilder> mPointers;
};
-static int32_t injectMotionEvent(
+static InputEventInjectionResult injectMotionEvent(
const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT) {
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
injectionTimeout,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
-static int32_t injectMotionEvent(
+static InputEventInjectionResult injectMotionEvent(
const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
const PointF& position,
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
- int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
MotionEvent event = MotionEventBuilder(action, source)
.displayId(displayId)
@@ -1087,13 +1041,15 @@
return injectMotionEvent(dispatcher, event);
}
-static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId, const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
+ int32_t source, int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
-static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
- int32_t displayId, const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher,
+ int32_t source, int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
@@ -1150,9 +1106,9 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1174,10 +1130,10 @@
window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1198,10 +1154,10 @@
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{50, 50}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1216,89 +1172,15 @@
ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowSecond->assertNoEvents();
}
-TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
-
- // Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-
- // Display should have only one focused window
- windowSecond->setFocusable(true);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
-
- windowSecond->consumeFocusEvent(true);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-
- // Focused window should receive event.
- windowTop->assertNoEvents();
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
-}
-
-TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
-
- // Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-
- // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
- windowTop->setFocusable(true);
- windowSecond->setFocusable(true);
-
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
- windowTop->consumeFocusEvent(true);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-
- // Top focused window should receive event.
- windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
- windowSecond->assertNoEvents();
-}
-
-TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
- ADISPLAY_ID_DEFAULT);
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
-
- // Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-
- windowTop->setFocusable(true);
- windowSecond->setFocusable(true);
- // Release channel for window is no longer valid.
- windowTop->releaseChannel();
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
- windowSecond->consumeFocusEvent(true);
-
- // Test inject a key down, should dispatch to a valid window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-
- // Top window is invalid, so it should not receive any input event.
- windowTop->assertNoEvents();
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
-}
-
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
@@ -1315,7 +1197,7 @@
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
// Start cursor position in right window so that we can move the cursor to left window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
@@ -1329,7 +1211,7 @@
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
// Move cursor into left window
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
@@ -1345,7 +1227,7 @@
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
// Inject a series of mouse events for a mouse click
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -1355,7 +1237,7 @@
.build()));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
@@ -1368,7 +1250,7 @@
windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
@@ -1381,7 +1263,7 @@
windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -1392,7 +1274,7 @@
windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
// Move mouse cursor back to right window
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
@@ -1421,7 +1303,7 @@
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
AINPUT_SOURCE_MOUSE)
@@ -1433,7 +1315,7 @@
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
// Inject a series of mouse events for a mouse click
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
@@ -1443,7 +1325,7 @@
.build()));
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
AINPUT_SOURCE_MOUSE)
@@ -1456,7 +1338,7 @@
window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
AINPUT_SOURCE_MOUSE)
@@ -1469,7 +1351,7 @@
window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
@@ -1479,7 +1361,7 @@
.build()));
window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
AINPUT_SOURCE_MOUSE)
@@ -1509,7 +1391,7 @@
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1523,6 +1405,8 @@
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
@@ -1724,6 +1608,7 @@
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -1774,10 +1659,9 @@
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
- dispatcher->registerInputMonitor(std::move(serverChannel), displayId, isGestureMonitor);
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher->createInputMonitor(displayId, isGestureMonitor, name);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
}
sp<IBinder> getToken() { return mInputReceiver->getToken(); }
@@ -1817,9 +1701,9 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
@@ -1833,13 +1717,15 @@
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
monitor.assertNoEvents();
}
@@ -1853,9 +1739,9 @@
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -1863,9 +1749,9 @@
mDispatcher->pilferPointers(monitor.getToken());
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
@@ -1874,7 +1760,7 @@
FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
@@ -1926,6 +1812,8 @@
SCOPED_TRACE("Check default value of touch mode");
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
@@ -1937,6 +1825,7 @@
mDispatcher->setInTouchMode(false);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
@@ -1948,6 +1837,7 @@
mDispatcher->setInTouchMode(true);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
window->assertNoEvents();
@@ -1962,6 +1852,8 @@
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
@@ -2027,6 +1919,208 @@
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(InputDispatcherTest, GeneratedHmac_IsConsistent) {
+ KeyEvent event = getTestKeyEvent();
+ VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+
+ std::array<uint8_t, 32> hmac1 = mDispatcher->sign(verifiedEvent);
+ std::array<uint8_t, 32> hmac2 = mDispatcher->sign(verifiedEvent);
+ ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in VerifiedKeyEvent produce a different hmac.
+ */
+TEST_F(InputDispatcherTest, GeneratedHmac_ChangesWhenFieldsChange) {
+ KeyEvent event = getTestKeyEvent();
+ VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+ std::array<uint8_t, 32> initialHmac = mDispatcher->sign(verifiedEvent);
+
+ verifiedEvent.deviceId += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.source += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.eventTimeNanos += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.displayId += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.action += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.downTimeNanos += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.flags += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.keyCode += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.scanCode += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.metaState += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+
+ verifiedEvent.repeatCount += 1;
+ ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ // Top window is also focusable but is not granted focus.
+ windowTop->setFocusable(true);
+ windowSecond->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ setFocusedWindow(windowSecond);
+
+ windowSecond->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Focused window should receive event.
+ windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowTop->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ window->setFocusable(true);
+ // Release channel for window is no longer valid.
+ window->releaseChannel();
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ // Test inject a key down, should timeout.
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+ // window channel is invalid, so it should not receive any input event.
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ // Window is not focusable.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ // Test inject a key down, should timeout.
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+ // window is invalid, so it should not receive any input event.
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ windowTop->setFocusable(true);
+ windowSecond->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ setFocusedWindow(windowTop);
+ windowTop->consumeFocusEvent(true);
+
+ setFocusedWindow(windowSecond, windowTop);
+ windowSecond->consumeFocusEvent(true);
+ windowTop->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Focused window should receive event.
+ windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowTop =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ windowTop->setFocusable(true);
+ windowSecond->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+ setFocusedWindow(windowSecond, windowTop);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+
+ // Event should be dropped.
+ windowTop->assertNoEvents();
+ windowSecond->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> previousFocusedWindow =
+ new FakeWindowHandle(application, mDispatcher, "previousFocusedWindow",
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ window->setFocusable(true);
+ previousFocusedWindow->setFocusable(true);
+ window->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, previousFocusedWindow}}});
+ setFocusedWindow(previousFocusedWindow);
+ previousFocusedWindow->consumeFocusEvent(true);
+
+ // Requesting focus on invisible window takes focus from currently focused window.
+ setFocusedWindow(window);
+ previousFocusedWindow->consumeFocusEvent(false);
+
+ // Injected key goes to pending queue.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+
+ // Window does not get focus event or key down.
+ window->assertNoEvents();
+
+ // Window becomes visible.
+ window->setVisible(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Window receives focus event.
+ window->consumeFocusEvent(true);
+ // Focused window receives key down.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
@@ -2051,12 +2145,13 @@
mWindow->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
-
+ setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
- void sendAndConsumeKeyDown() {
+ void sendAndConsumeKeyDown(int32_t deviceId) {
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
mDispatcher->notifyKey(&keyArgs);
@@ -2078,8 +2173,9 @@
EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
}
- void sendAndConsumeKeyUp() {
+ void sendAndConsumeKeyUp(int32_t deviceId) {
NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
mDispatcher->notifyKey(&keyArgs);
@@ -2090,21 +2186,59 @@
};
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeatFromTwoDevices) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ /* repeatCount will start from 1 for deviceId 2 */
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
expectKeyRepeatOnce(repeatCount);
}
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
expectKeyRepeatOnce(1 /*repeatCount*/);
- sendAndConsumeKeyUp();
+ sendAndConsumeKeyUp(1 /* deviceId */);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ // Stale key up from device 1.
+ sendAndConsumeKeyUp(1 /* deviceId */);
+ // Device 2 is still down, keep repeating
+ expectKeyRepeatOnce(2 /*repeatCount*/);
+ expectKeyRepeatOnce(3 /*repeatCount*/);
+ // Device 2 key up
+ sendAndConsumeKeyUp(2 /* deviceId */);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatStopsAfterRepeatingKeyUp) {
+ sendAndConsumeKeyDown(1 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ sendAndConsumeKeyDown(2 /* deviceId */);
+ expectKeyRepeatOnce(1 /*repeatCount*/);
+ // Device 2 which holds the key repeating goes up, expect the repeating to stop.
+ sendAndConsumeKeyUp(2 /* deviceId */);
+ // Device 1 still holds key down, but the repeating was already stopped
mWindow->assertNoEvents();
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
InputEvent* repeatEvent = mWindow->consume();
ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
@@ -2114,7 +2248,7 @@
}
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) {
- sendAndConsumeKeyDown();
+ sendAndConsumeKeyDown(1 /* deviceId */);
std::unordered_set<int32_t> idSet;
for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
@@ -2141,6 +2275,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
windowInPrimary->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
+ setFocusedWindow(windowInPrimary);
windowInPrimary->consumeFocusEvent(true);
application2 = std::make_shared<FakeApplicationHandle>();
@@ -2153,6 +2288,7 @@
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocusable(true);
mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ setFocusedWindow(windowInSecondary);
windowInSecondary->consumeFocusEvent(true);
}
@@ -2174,43 +2310,43 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
// Test touch down on primary display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
}
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
// Remove all windows in secondary display.
mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
- // Expect old focus should receive a cancel event.
+ // Old focus should receive a cancel event.
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeFocusEvent(false);
windowInSecondary->assertNoEvents();
@@ -2224,18 +2360,18 @@
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
monitorInSecondary.assertNoEvents();
// Test touch down on second display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
@@ -2244,9 +2380,9 @@
// Test inject a non-pointer motion event.
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
@@ -2262,14 +2398,31 @@
FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
}
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) {
+ sp<FakeWindowHandle> secondWindowInPrimary =
+ new FakeWindowHandle(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ secondWindowInPrimary->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}});
+ setFocusedWindow(secondWindowInPrimary);
+ windowInPrimary->consumeFocusEvent(false);
+ secondWindowInPrimary->consumeFocusEvent(true);
+
+ // Test inject a key down.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ windowInSecondary->assertNoEvents();
+ secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
class InputFilterTest : public InputDispatcherTest {
protected:
static constexpr int32_t SECOND_DISPLAY_ID = 1;
@@ -2367,6 +2520,7 @@
// Expect one focus window exist in display.
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -2387,10 +2541,10 @@
// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{20, 20}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2401,9 +2555,9 @@
// DOWN on the window that doesn't have focus. Ensure no window received the
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2413,8 +2567,8 @@
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
- << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2426,10 +2580,10 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus,
OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_TOUCH_POINT))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2660,6 +2814,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -2674,10 +2829,10 @@
static constexpr PointF WINDOW_LOCATION = {20, 20};
void tapOnWindow() {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
}
@@ -2694,17 +2849,37 @@
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
+TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) {
+ mWindow->setFocusable(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ mWindow->consumeFocusEvent(false);
+
+ InputEventInjectionResult result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+ // Key will not go to window because we have no focused window.
+ // The 'no focused window' ANR timer should start instead.
+
+ // Now, the focused application goes away.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, nullptr);
+ // The key should get dropped and there should be no ANR.
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
// Send an event to the app and have the app not respond right away.
// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
@@ -2723,7 +2898,7 @@
// Send a key to the app and have the app not respond right away.
TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
// Inject a key, and don't respond - expect that ANR is called.
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
ASSERT_TRUE(sequenceNum);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -2738,7 +2913,7 @@
mWindow->consumeFocusEvent(false);
// taps on the window work as normal
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
@@ -2748,10 +2923,10 @@
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
- const int32_t result =
+ const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -2770,10 +2945,10 @@
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
- const int32_t result =
+ const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration appTimeout =
mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
@@ -2794,16 +2969,16 @@
mWindow->consumeFocusEvent(false);
// Once a focused event arrives, we get an ANR for this application
- const int32_t result =
+ const InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
// Future focused events get dropped right away
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher));
+ ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
ASSERT_TRUE(mDispatcher->waitForIdle());
mWindow->assertNoEvents();
}
@@ -2823,14 +2998,14 @@
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
- 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+ 500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// Now send ACTION_UP, with identical timestamp
injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
- 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+ 500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// We have now sent down and up. Let's consume first event and then ANR on the second.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -2845,9 +3020,10 @@
FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
true /*isGestureMonitor*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -2923,7 +3099,7 @@
const std::chrono::duration timeout = 5ms;
mFakePolicy->setAnrTimeout(timeout);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
WINDOW_LOCATION));
@@ -2971,10 +3147,10 @@
// window even if motions are still being processed. But because the injection timeout is short,
// we will receive INJECTION_TIMED_OUT as the result.
- int32_t result =
+ InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
- ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
@@ -3006,9 +3182,9 @@
ASSERT_TRUE(upSequenceNum);
// Don't finish the events yet, and send a key
// Injection is async, so it will succeed
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
- ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE));
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
// At this point, key is still pending, and should not be sent to the application yet.
std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
ASSERT_FALSE(keySequenceNum);
@@ -3053,6 +3229,7 @@
// Expect one focus window exist in display.
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -3077,10 +3254,10 @@
private:
void tap(const PointF& location) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
location));
}
@@ -3089,10 +3266,10 @@
// If we have 2 windows that are both unresponsive, the one with the shortest timeout
// should be ANR'd first.
TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION))
- << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
ADISPLAY_ID_DEFAULT, 0 /*flags*/);
@@ -3100,7 +3277,7 @@
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
@@ -3162,10 +3339,10 @@
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
// Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
@@ -3184,7 +3361,7 @@
// If you tap outside of all windows, there will not be ANR
TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
LOCATION_OUTSIDE_ALL_WINDOWS));
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -3196,7 +3373,7 @@
mFocusedWindow->setPaused(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
- ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
FOCUSED_WINDOW_LOCATION));
@@ -3236,10 +3413,10 @@
// Injection will succeed because we will eventually give up and send the key to the focused
// window even if motions are still being processed.
- int32_t result =
+ InputEventInjectionResult result =
injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
- INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not be sent to the window, yet, because the window is still processing events
// and the key remains pending, waiting for the touch events to be processed
std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
@@ -3249,6 +3426,7 @@
mFocusedWindow->setFocusable(false);
mUnfocusedWindow->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ setFocusedWindow(mUnfocusedWindow);
// Focus events should precede the key events
mUnfocusedWindow->consumeFocusEvent(true);
@@ -3313,6 +3491,68 @@
mFocusedWindow->assertNoEvents();
}
+/**
+ * If we have no focused window, and a key comes in, we start the ANR timer.
+ * The focused application should add a focused window before the timer runs out to prevent ANR.
+ *
+ * If the user touches another application during this time, the key should be dropped.
+ * Next, if a new focused window comes in, without toggling the focused application,
+ * then no ANR should occur.
+ *
+ * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication',
+ * but in some cases the policy may not update the focused application.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
+ std::shared_ptr<FakeApplicationHandle> focusedApplication =
+ std::make_shared<FakeApplicationHandle>();
+ focusedApplication->setDispatchingTimeout(60ms);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+ // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
+ mFocusedWindow->setFocusable(false);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Send a key. The ANR timer should start because there is no focused window.
+ // 'focusedApplication' will get blamed if this timer completes.
+ // Key will not be sent anywhere because we have no focused window. It will remain pending.
+ InputEventInjectionResult result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
+
+ // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
+ // then the injected touches won't cause the focused event to get dropped.
+ // The dispatcher only checks for whether the queue should be pruned upon queueing.
+ // If we inject the touch right away and the ANR timer hasn't started, the touch event would
+ // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
+ // For this test, it means that the key would get delivered to the window once it becomes
+ // focused.
+ std::this_thread::sleep_for(10ms);
+
+ // Touch unfocused window. This should force the pending key to get dropped.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // We do not consume the motion right away, because that would require dispatcher to first
+ // process (== drop) the key event, and by that time, ANR will be raised.
+ // Set the focused window first.
+ mFocusedWindow->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
+ mFocusedWindow->consumeFocusEvent(true);
+ // We do not call "setFocusedApplication" here, even though the newly focused window belongs
+ // to another application. This could be a bug / behaviour in the policy.
+
+ mUnfocusedWindow->consumeMotionDown();
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because we actually have a focused window. It was just added too slowly.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
+}
+
// These tests ensure we cannot send touch events to a window that's positioned behind a window
// that has feature NO_INPUT_CHANNEL.
// Layout:
@@ -3386,4 +3626,147 @@
mBottomWindow->assertNoEvents();
}
+class InputDispatcherMirrorWindowFocusTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mMirror;
+
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mMirror = new FakeWindowHandle(mApp, mDispatcher, "TestWindowMirror", ADISPLAY_ID_DEFAULT,
+ mWindow->getToken());
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mWindow->setFocusable(true);
+ mMirror->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ }
+};
+
+TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) {
+ // Request focus on a mirrored window
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+// A focused & mirrored window remains focused only if the window and its mirror are both
+// focusable.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) {
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mMirror->setFocusable(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ // window loses focus since one of the windows associated with the token in not focusable
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ mWindow->assertNoEvents();
+}
+
+// A focused & mirrored window remains focused until the window and its mirror both become
+// invisible.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAnyWindowVisible) {
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mMirror->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mWindow->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ // window loses focus only after all windows associated with the token become invisible.
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ mWindow->assertNoEvents();
+}
+
+// A focused & mirrored window remains focused until both windows are removed.
+TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) {
+ setFocusedWindow(mMirror);
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ // single window is removed but the window token remains focused
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ // Both windows are removed
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+ mWindow->assertNoEvents();
+}
+
+// Focus request can be pending until one window becomes visible.
+TEST_F(InputDispatcherMirrorWindowFocusTests, DeferFocusWhenInvisible) {
+ // Request focus on an invisible mirror.
+ mWindow->setVisible(false);
+ mMirror->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+ setFocusedWindow(mMirror);
+
+ // Injected key goes to pending queue.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+
+ mMirror->setVisible(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ // window gets focused
+ mWindow->consumeFocusEvent(true);
+ // window gets the pending key event
+ mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index 99b96e9..c368e79 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -135,7 +135,6 @@
public:
TestInputManager(){};
- void checkFdFlags(const android::base::unique_fd& fd);
binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
@@ -147,10 +146,12 @@
const std::vector<InputWindowInfo>& handles,
const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
- binder::Status registerInputChannel(const InputChannel& channel) override;
- binder::Status unregisterInputChannel(const InputChannel& channel) 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::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
@@ -164,6 +165,7 @@
binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
binder::Status getLastFocusRequest(FocusRequest*) override;
+ binder::Status resetInputManager() override;
private:
sp<android::TestInputManager> mManager;
@@ -182,6 +184,11 @@
return mManager->getLastFocusRequest(request);
}
+binder::Status TestInputQuery::resetInputManager() {
+ mManager->reset();
+ return binder::Status::ok();
+}
+
binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
if (mCbFunc != nullptr) {
mCbFunc();
@@ -204,29 +211,27 @@
return binder::Status::ok();
}
-void TestInputManager::checkFdFlags(const android::base::unique_fd& fd) {
- const int result = fcntl(fd, F_GETFL);
- EXPECT_NE(result, -1);
- EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
-}
-
-binder::Status TestInputManager::registerInputChannel(const InputChannel& channel) {
+binder::Status TestInputManager::createInputChannel(const std::string& name,
+ InputChannel* outChannel) {
AutoMutex _l(mLock);
- // check Fd flags
- checkFdFlags(channel.getFd());
+ std::unique_ptr<InputChannel> serverChannel;
+ std::unique_ptr<InputChannel> clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
- mInputChannels.push_back(channel.dup());
+ clientChannel->copyTo(*outChannel);
+
+ mInputChannels.emplace_back(std::move(serverChannel));
return binder::Status::ok();
}
-binder::Status TestInputManager::unregisterInputChannel(const InputChannel& channel) {
+binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
AutoMutex _l(mLock);
- // check Fd flags
- checkFdFlags(channel.getFd());
auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
- [&](std::shared_ptr<InputChannel>& c) { return *c == channel; });
+ [&](std::shared_ptr<InputChannel>& c) {
+ return c->getConnectionToken() == connectionToken;
+ });
if (it != mInputChannels.end()) {
mInputChannels.erase(it);
}
@@ -271,6 +276,12 @@
return binder::Status::ok();
}
+void TestInputManager::reset() {
+ mHandlesPerDisplay.clear();
+ mInputChannels.clear();
+ mFocusRequest = FocusRequest();
+}
+
void InputFlingerServiceTest::SetUp() {
mSetInputWindowsListener = new SetInputWindowsListener([&]() {
std::unique_lock<std::mutex> lock(mLock);
@@ -316,7 +327,9 @@
InitializeInputFlinger();
}
-void InputFlingerServiceTest::TearDown() {}
+void InputFlingerServiceTest::TearDown() {
+ mQuery->resetInputManager();
+}
void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
EXPECT_EQ(mInfo, info);
@@ -367,45 +380,33 @@
}
/**
- * Test InputFlinger service interface registerInputChannel
+ * Test InputFlinger service interface createInputChannel
*/
-TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannel) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
+TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
+ // Test that the unblocked file descriptor flag is kept across processes over binder
+ // transactions.
- InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
- mService->registerInputChannel(*serverChannel);
+ 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, InputWindow_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], *serverChannel);
+ EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
- mService->unregisterInputChannel(*serverChannel);
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 0UL);
-}
-
-/**
- * Test InputFlinger service interface registerInputChannel with invalid cases
- */
-TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannelInvalid) {
- std::unique_ptr<InputChannel> serverChannel, clientChannel;
- InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
-
- std::vector<::android::InputChannel> channels;
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 0UL);
-
- mService->registerInputChannel(InputChannel());
- mService->unregisterInputChannel(*clientChannel);
-
- mService->registerInputChannel(*serverChannel);
- mService->registerInputChannel(*clientChannel);
- mQuery->getInputChannels(&channels);
- EXPECT_EQ(channels.size(), 2UL);
-
- mService->unregisterInputChannel(*clientChannel);
- mService->unregisterInputChannel(*serverChannel);
+ mService->removeInputChannel(channel.getConnectionToken());
mQuery->getInputChannels(&channels);
EXPECT_EQ(channels.size(), 0UL);
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 3872de5..211b49e 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -102,29 +102,23 @@
mMaxY = maxY;
}
- virtual void setPosition(float x, float y) {
+ void setPosition(float x, float y) override {
mX = x;
mY = y;
}
- virtual void setButtonState(int32_t buttonState) {
- mButtonState = buttonState;
- }
+ void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
- virtual int32_t getButtonState() const {
- return mButtonState;
- }
+ int32_t getButtonState() const override { return mButtonState; }
- virtual void getPosition(float* outX, float* outY) const {
+ void getPosition(float* outX, float* outY) const override {
*outX = mX;
*outY = mY;
}
- virtual int32_t getDisplayId() const {
- return mDisplayId;
- }
+ int32_t getDisplayId() const override { return mDisplayId; }
- virtual void setDisplayViewport(const DisplayViewport& viewport) {
+ void setDisplayViewport(const DisplayViewport& viewport) override {
mDisplayId = viewport.displayId;
}
@@ -133,7 +127,7 @@
}
private:
- virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
+ bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
*outMinX = mMinX;
*outMinY = mMinY;
*outMaxX = mMaxX;
@@ -141,7 +135,7 @@
return mHaveBounds;
}
- virtual void move(float deltaX, float deltaY) {
+ void move(float deltaX, float deltaY) override {
mX += deltaX;
if (mX < mMinX) mX = mMinX;
if (mX > mMaxX) mX = mMaxX;
@@ -150,17 +144,14 @@
if (mY > mMaxY) mY = mMaxY;
}
- virtual void fade(Transition) {
- }
+ void fade(Transition) override {}
- virtual void unfade(Transition) {
- }
+ void unfade(Transition) override {}
- virtual void setPresentation(Presentation) {
- }
+ void setPresentation(Presentation) override {}
- virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
- int32_t displayId) {
+ 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(); ) {
@@ -171,8 +162,7 @@
mSpotsByDisplay[displayId] = newSpots;
}
- virtual void clearSpots() {
- }
+ void clearSpots() override {}
std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
};
@@ -192,7 +182,7 @@
TouchAffineTransformation transform;
protected:
- virtual ~FakeInputReaderPolicy() { }
+ virtual ~FakeInputReaderPolicy() {}
public:
FakeInputReaderPolicy() {
@@ -325,28 +315,27 @@
return v;
}
- virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
+ void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
return mPointerControllers[deviceId];
}
- virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
std::scoped_lock<std::mutex> lock(mLock);
mInputDevices = inputDevices;
mInputDevicesChanged = true;
mDevicesChangedCondition.notify_all();
}
- virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
+ std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) override {
return nullptr;
}
- virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
- return "";
- }
+ std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
std::unique_lock<std::mutex> lock(mLock);
@@ -591,29 +580,27 @@
return index >= 0 ? mDevices.valueAt(index) : nullptr;
}
- virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const {
+ Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
Device* device = getDevice(deviceId);
return device ? device->classes : Flags<InputDeviceClass>(0);
}
- virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const {
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
Device* device = getDevice(deviceId);
return device ? device->identifier : InputDeviceIdentifier();
}
- virtual int32_t getDeviceControllerNumber(int32_t) const {
- return 0;
- }
+ int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
- virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
Device* device = getDevice(deviceId);
if (device) {
*outConfiguration = device->configuration;
}
}
- virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const {
+ 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);
@@ -626,7 +613,7 @@
return -1;
}
- virtual bool hasRelativeAxis(int32_t deviceId, int axis) const {
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override {
Device* device = getDevice(deviceId);
if (device) {
return device->relativeAxes.indexOfKey(axis) >= 0;
@@ -634,13 +621,10 @@
return false;
}
- virtual bool hasInputProperty(int32_t, int) const {
- return false;
- }
+ bool hasInputProperty(int32_t, int) const override { return false; }
- 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 {
+ 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);
@@ -676,15 +660,13 @@
return nullptr;
}
- virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const {
- return NAME_NOT_FOUND;
- }
+ status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
- virtual void setExcludedDevices(const std::vector<std::string>& devices) {
+ void setExcludedDevices(const std::vector<std::string>& devices) override {
mExcludedDevices = devices;
}
- virtual size_t getEvents(int, RawEvent* buffer, size_t) {
+ size_t getEvents(int, RawEvent* buffer, size_t) override {
std::scoped_lock<std::mutex> lock(mLock);
if (mEvents.empty()) {
return 0;
@@ -696,7 +678,7 @@
return 1;
}
- virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+ 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);
@@ -706,7 +688,7 @@
return {};
}
- virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
@@ -717,7 +699,7 @@
return AKEY_STATE_UNKNOWN;
}
- virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
@@ -728,7 +710,7 @@
return AKEY_STATE_UNKNOWN;
}
- virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const {
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->switchStates.indexOfKey(sw);
@@ -739,8 +721,8 @@
return AKEY_STATE_UNKNOWN;
}
- virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const {
+ 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);
@@ -753,22 +735,22 @@
return -1;
}
- virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
- uint8_t* outFlags) const {
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const 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 < numCodes; i++) {
for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
outFlags[i] = 1;
- result = true;
}
}
for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
outFlags[i] = 1;
- result = true;
}
}
}
@@ -776,7 +758,7 @@
return result;
}
- virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const {
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
@@ -785,12 +767,12 @@
return false;
}
- virtual bool hasLed(int32_t deviceId, int32_t led) const {
+ bool hasLed(int32_t deviceId, int32_t led) const override {
Device* device = getDevice(deviceId);
return device && device->leds.indexOfKey(led) >= 0;
}
- virtual void setLedState(int32_t deviceId, int32_t led, bool on) {
+ void setLedState(int32_t deviceId, int32_t led, bool on) override {
Device* device = getDevice(deviceId);
if (device) {
ssize_t index = device->leds.indexOfKey(led);
@@ -804,8 +786,8 @@
}
}
- virtual void getVirtualKeyDefinitions(int32_t deviceId,
- std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ void getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
outVirtualKeys.clear();
Device* device = getDevice(deviceId);
@@ -814,145 +796,31 @@
}
}
- virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const {
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
return nullptr;
}
- virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) {
+ bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
return false;
}
- virtual void vibrate(int32_t, const VibrationElement&) {}
+ void vibrate(int32_t, const VibrationElement&) override {}
- virtual void cancelVibrate(int32_t) {
- }
+ void cancelVibrate(int32_t) override {}
virtual bool isExternal(int32_t) const {
return false;
}
- virtual void dump(std::string&) {
- }
+ void dump(std::string&) override {}
- virtual void monitor() {
- }
+ void monitor() override {}
- virtual void requestReopenDevices() {
- }
+ void requestReopenDevices() override {}
- virtual void wake() {
- }
+ void wake() override {}
};
-
-// --- FakeInputReaderContext ---
-
-class FakeInputReaderContext : public InputReaderContext {
- std::shared_ptr<EventHubInterface> mEventHub;
- sp<InputReaderPolicyInterface> mPolicy;
- sp<InputListenerInterface> mListener;
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
- int32_t mNextId;
- std::weak_ptr<PointerControllerInterface> mPointerController;
-
-public:
- FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener)
- : mEventHub(eventHub),
- mPolicy(policy),
- mListener(listener),
- mGlobalMetaState(0),
- mNextId(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 updatePointerDisplay() {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller != nullptr) {
- InputReaderConfiguration config;
- mPolicy->getReaderConfiguration(&config);
- auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId);
- if (viewport) {
- controller->setDisplayViewport(*viewport);
- }
- }
- }
-
-private:
- virtual void updateGlobalMetaState() {
- mUpdateGlobalMetaStateWasCalled = true;
- }
-
- virtual int32_t getGlobalMetaState() {
- return mGlobalMetaState;
- }
-
- virtual EventHubInterface* getEventHub() {
- return mEventHub.get();
- }
-
- virtual InputReaderPolicyInterface* getPolicy() {
- return mPolicy.get();
- }
-
- virtual InputListenerInterface* getListener() {
- return mListener.get();
- }
-
- virtual void disableVirtualKeysUntil(nsecs_t) {
- }
-
- virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
-
- virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller == nullptr) {
- controller = mPolicy->obtainPointerController(deviceId);
- mPointerController = controller;
- updatePointerDisplay();
- }
- return controller;
- }
-
- virtual void fadePointer() {
- }
-
- virtual void requestTimeoutAtTime(nsecs_t) {
- }
-
- virtual int32_t bumpGeneration() {
- return ++mGeneration;
- }
-
- virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
-
- }
-
- virtual void dispatchExternalStylusState(const StylusState&) {
-
- }
-
- virtual int32_t getNextId() { return mNextId++; }
-};
-
-
// --- FakeInputMapper ---
class FakeInputMapper : public InputMapper {
@@ -982,7 +850,7 @@
mResetWasCalled(false),
mProcessWasCalled(false) {}
- virtual ~FakeInputMapper() { }
+ virtual ~FakeInputMapper() {}
void setKeyboardType(int32_t keyboardType) {
mKeyboardType = keyboardType;
@@ -1051,11 +919,9 @@
}
private:
- virtual uint32_t getSources() {
- return mSources;
- }
+ uint32_t getSources() override { return mSources; }
- virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
InputMapper::populateDeviceInfo(deviceInfo);
if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
@@ -1063,7 +929,7 @@
}
}
- virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+ void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
std::scoped_lock<std::mutex> lock(mLock);
mConfigureWasCalled = true;
@@ -1076,45 +942,45 @@
mStateChangedCondition.notify_all();
}
- virtual void reset(nsecs_t) {
+ void reset(nsecs_t) override {
std::scoped_lock<std::mutex> lock(mLock);
mResetWasCalled = true;
mStateChangedCondition.notify_all();
}
- virtual void process(const RawEvent* rawEvent) {
+ void process(const RawEvent* rawEvent) override {
std::scoped_lock<std::mutex> lock(mLock);
mLastEvent = *rawEvent;
mProcessWasCalled = true;
mStateChangedCondition.notify_all();
}
- virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
+ int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
}
- virtual int32_t getScanCodeState(uint32_t, int32_t scanCode) {
+ int32_t getScanCodeState(uint32_t, int32_t scanCode) override {
ssize_t index = mScanCodeStates.indexOfKey(scanCode);
return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
}
- virtual int32_t getSwitchState(uint32_t, int32_t switchCode) {
+ int32_t getSwitchState(uint32_t, int32_t switchCode) override {
ssize_t index = mSwitchStates.indexOfKey(switchCode);
return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
}
- virtual bool markSupportedKeyCodes(uint32_t, size_t numCodes,
- const int32_t* keyCodes, uint8_t* outFlags) {
- bool result = false;
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) override {
for (size_t i = 0; i < numCodes; i++) {
for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
if (keyCodes[i] == mSupportedKeyCodes[j]) {
outFlags[i] = 1;
- result = true;
}
}
}
+ bool result = mSupportedKeyCodes.size() > 0;
return result;
}
@@ -1143,7 +1009,7 @@
InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
- : InputReader(eventHub, policy, listener) {}
+ : InputReader(eventHub, policy, listener), mFakeContext(this) {}
virtual ~InstrumentedInputReader() {}
@@ -1155,7 +1021,7 @@
identifier.name = name;
identifier.location = location;
int32_t generation = deviceId + 1;
- return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier);
+ return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
}
// Make the protected loopOnce method accessible to tests.
@@ -1172,7 +1038,50 @@
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 ---
@@ -1180,8 +1089,8 @@
protected:
sp<FakeInputReaderPolicy> mFakePolicy;
- virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
- virtual void TearDown() override { mFakePolicy.clear(); }
+ void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+ void TearDown() override { mFakePolicy.clear(); }
};
/**
@@ -1371,7 +1280,7 @@
std::shared_ptr<FakeEventHub> mFakeEventHub;
std::unique_ptr<InstrumentedInputReader> mReader;
- virtual void SetUp() override {
+ void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1380,7 +1289,7 @@
mFakeListener);
}
- virtual void TearDown() override {
+ void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -1421,22 +1330,27 @@
}
};
-TEST_F(InputReaderTest, GetInputDevices) {
+TEST_F(InputReaderTest, ReaderGetInputDevices) {
ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
nullptr)); // no classes so device will be ignored
- std::vector<InputDeviceInfo> inputDevices;
- mReader->getInputDevices(inputDevices);
+ const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices();
ASSERT_EQ(1U, inputDevices.size());
ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
+}
+
+TEST_F(InputReaderTest, PolicyGetInputDevices) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0),
+ nullptr)); // no classes so device will be ignored
// Should also have received a notification describing the new input devices.
- inputDevices = mFakePolicy->getInputDevices();
+ const std::vector<InputDeviceInfo>& inputDevices = mFakePolicy->getInputDevices();
ASSERT_EQ(1U, inputDevices.size());
ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
@@ -1445,6 +1359,27 @@
ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, GetMergedInputDevices) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+ // Add two subdevices to device
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+ // Must add at least one mapper or the device will be ignored!
+ device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+ device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+ // Push same device instance for next device to be added, so they'll have same identifier.
+ mReader->pushNextDevice(device);
+ mReader->pushNextDevice(device);
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[0], "fake1", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+
+ // Two devices will be merged to one input device as they have same identifier
+ ASSERT_EQ(1U, mReader->getInputDevices().size());
+}
+
TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
@@ -1827,7 +1762,7 @@
sp<FakeInputReaderPolicy> mFakePolicy;
sp<InputReaderInterface> mReader;
- virtual void SetUp() override {
+ void SetUp() override {
mFakePolicy = new FakeInputReaderPolicy();
mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
30ms /*eventDidNotHappenTimeout*/);
@@ -1842,7 +1777,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
}
- virtual void TearDown() override {
+ void TearDown() override {
ASSERT_EQ(mReader->stop(), OK);
mTestListener.clear();
mFakePolicy.clear();
@@ -1883,20 +1818,17 @@
ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
// Find the test device by its name.
- std::vector<InputDeviceInfo> inputDevices;
- mReader->getInputDevices(inputDevices);
- InputDeviceInfo* keyboardInfo = nullptr;
- const char* keyboardName = keyboard->getName();
- for (unsigned int i = 0; i < initialNumDevices + 1; i++) {
- if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) {
- keyboardInfo = &inputDevices[i];
- break;
- }
- }
- ASSERT_NE(keyboardInfo, nullptr);
- ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType());
- ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources());
- ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size());
+ const std::vector<InputDeviceInfo> inputDevices = mReader->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());
keyboard.reset();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -1956,7 +1888,7 @@
protected:
const std::string UNIQUE_ID = "local:0";
- virtual void SetUp() override {
+ void SetUp() override {
InputReaderIntegrationTest::SetUp();
// At least add an internal display.
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
@@ -2097,27 +2029,26 @@
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
sp<TestInputListener> mFakeListener;
- FakeInputReaderContext* mFakeContext;
-
+ std::unique_ptr<InstrumentedInputReader> mReader;
std::shared_ptr<InputDevice> mDevice;
- virtual void SetUp() override {
+ void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-
- mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
identifier.location = DEVICE_LOCATION;
- mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+ mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
identifier);
+ mReader->pushNextDevice(mDevice);
+ mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
+ mReader->loopOnce();
}
- virtual void TearDown() override {
- mDevice = nullptr;
- delete mFakeContext;
+ void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -2336,27 +2267,21 @@
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
sp<TestInputListener> mFakeListener;
- FakeInputReaderContext* mFakeContext;
- InputDevice* mDevice;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
virtual void SetUp(Flags<InputDeviceClass> classes) {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
- InputDeviceIdentifier identifier;
- identifier.name = DEVICE_NAME;
- identifier.location = DEVICE_LOCATION;
- mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier);
-
- mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes);
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
}
- virtual void SetUp() override { SetUp(DEVICE_CLASSES); }
+ void SetUp() override { SetUp(DEVICE_CLASSES); }
- virtual void TearDown() override {
- delete mDevice;
- delete mFakeContext;
+ void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -2367,11 +2292,27 @@
void configureDevice(uint32_t changes) {
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mFakeContext->updatePointerDisplay();
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
}
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
}
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ 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...);
@@ -2393,8 +2334,7 @@
mFakePolicy->clearViewports();
}
- static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
- int32_t value) {
+ void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, int32_t value) {
RawEvent event;
event.when = when;
event.deviceId = mapper.getDeviceContext().getEventHubId();
@@ -2402,6 +2342,7 @@
event.code = code;
event.value = value;
mapper.process(&event);
+ mReader->loopOnce();
}
static void assertMotionRange(const InputDeviceInfo& info,
@@ -2543,10 +2484,16 @@
const int32_t USAGE_UNKNOWN = 0x07ffff;
mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initial metastate to AMETA_NONE.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Key down by scan code.
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
@@ -2641,13 +2588,17 @@
TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
KeyboardInputMapper& mapper =
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- // Initial metastate.
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ // Initial metastate to AMETA_NONE.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Metakey down.
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
@@ -2655,7 +2606,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
// Key down.
process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
@@ -2674,7 +2625,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_NONE, args.metaState);
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
}
TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
@@ -2879,6 +2830,9 @@
KeyboardInputMapper& mapper =
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initialize metastate to AMETA_NUM_LOCK_ON.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Initialization should have turned all of the lights off.
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
@@ -2934,6 +2888,43 @@
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
}
+TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+
+ // Initial metastate should be AMETA_NONE as no meta keys added.
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ // Meta state should be AMETA_NONE after reset
+ 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);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+ NotifyKeyArgs args;
+ // Press button "A"
+ process(mapper, ARBITRARY_TIME, EV_KEY, BTN_A, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+
+ // Button up.
+ process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_A, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+}
+
TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
// keyboard 1.
mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
@@ -2943,16 +2934,13 @@
// keyboard 2.
const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- InputDeviceIdentifier identifier;
- identifier.name = "KEYBOARD2";
- identifier.location = USB2;
- std::unique_ptr<InputDevice> device2 =
- std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
- identifier);
- mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME,
- Flags<InputDeviceClass>(0) /*classes*/);
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ Flags<InputDeviceClass>(0));
+
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
@@ -3015,13 +3003,80 @@
AKEYCODE_DPAD_LEFT, newDisplayId));
}
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initial metastate to AMETA_NONE.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+
+ // Initialization should have turned all of the lights off.
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+
+ // Toggle caps lock on.
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+
+ // Toggle num lock on.
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
+
+ // Toggle scroll lock on.
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+ mFakeEventHub->removeDevice(EVENTHUB_ID);
+ mReader->loopOnce();
+
+ // keyboard 2 should default toggle keys.
+ const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "KEYBOARD2";
+ constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+ constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ Flags<InputDeviceClass>(0));
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+ mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ 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);
+
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
+ ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
+ mapper2.getMetaState());
+}
+
// --- KeyboardInputMapperTest_ExternalDevice ---
class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
protected:
- virtual void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL);
- }
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
};
TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
@@ -3109,7 +3164,7 @@
std::shared_ptr<FakePointerController> mFakePointerController;
- virtual void SetUp() override {
+ void SetUp() override {
InputMapperTest::SetUp();
mFakePointerController = std::make_shared<FakePointerController>();
@@ -3211,7 +3266,7 @@
addConfigurationProperty("cursor.mode", "navigation");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs args;
@@ -3839,10 +3894,10 @@
// Disable pointer capture and check that the device generation got bumped
// and events are generated the usual way.
- const uint32_t generation = mFakeContext->getGeneration();
+ const uint32_t generation = mReader->getContext()->getGeneration();
mFakePolicy->setPointerCapture(false);
configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
- ASSERT_TRUE(mFakeContext->getGeneration() != generation);
+ ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
@@ -4257,7 +4312,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyKeyArgs args;
@@ -4307,7 +4362,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyKeyArgs keyArgs;
@@ -4428,7 +4483,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -4503,7 +4558,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -4599,7 +4654,7 @@
prepareVirtualKeys();
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -5494,7 +5549,7 @@
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -5770,7 +5825,7 @@
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -5945,7 +6000,7 @@
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+ mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
NotifyMotionArgs motionArgs;
@@ -6925,16 +6980,13 @@
// Create the second touch screen device, and enable multi fingers.
const std::string USB2 = "USB2";
+ const std::string DEVICE_NAME2 = "TOUCHSCREEN2";
constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- InputDeviceIdentifier identifier;
- identifier.name = "TOUCHSCREEN2";
- identifier.location = USB2;
- std::unique_ptr<InputDevice> device2 =
- std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
- identifier);
- mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME,
- Flags<InputDeviceClass>(0) /*classes*/);
+ std::shared_ptr<InputDevice> device2 =
+ newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+ Flags<InputDeviceClass>(0));
+
mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
0 /*flat*/, 0 /*fuzz*/);
mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
@@ -7446,9 +7498,7 @@
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
protected:
- virtual void SetUp() override {
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL);
- }
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
};
/**
@@ -7627,6 +7677,20 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+ InputDeviceInfo deviceInfo;
+ mDevice->getDeviceInfo(&deviceInfo);
+
+ const InputDeviceInfo::MotionRange* relRangeX =
+ deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(relRangeX, nullptr);
+ ASSERT_EQ(relRangeX->min, -(RAW_X_MAX - RAW_X_MIN));
+ ASSERT_EQ(relRangeX->max, RAW_X_MAX - RAW_X_MIN);
+ const InputDeviceInfo::MotionRange* relRangeY =
+ deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(relRangeY, nullptr);
+ ASSERT_EQ(relRangeY->min, -(RAW_Y_MAX - RAW_Y_MIN));
+ ASSERT_EQ(relRangeY->max, RAW_Y_MAX - RAW_Y_MIN);
+
// run captured pointer tests - note that this is unscaled, so input listener events should be
// identical to what the hardware sends (accounting for any
// calibration).
diff --git a/services/powermanager/benchmarks/AndroidTest.xml b/services/powermanager/benchmarks/AndroidTest.xml
deleted file mode 100644
index 40f4872..0000000
--- a/services/powermanager/benchmarks/AndroidTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<configuration description="Config for libpowermanager benchmarks">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="libpowermanager_benchmarks->/data/benchmarktest/benchmark" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-metric-instrumentation" />
- <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
- <option name="native-benchmark-device-path" value="/data/benchmarktest" />
- <option name="benchmark-module-name" value="benchmark" />
- </test>
-</configuration>
diff --git a/services/powermanager/tests/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp
index 575b9ee..b62be5f 100644
--- a/services/powermanager/tests/IThermalManagerTest.cpp
+++ b/services/powermanager/tests/IThermalManagerTest.cpp
@@ -86,12 +86,14 @@
EXPECT_NE(binder, nullptr);
mThermalSvc = interface_cast<IThermalService>(binder);
EXPECT_NE(mThermalSvc, nullptr);
+ // Lock mutex for operation, so listener will only be processed after wait_for is called
+ std::unique_lock<std::mutex> lock(mMutex);
bool success = false;
binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success);
+ // Check the result
ASSERT_TRUE(success);
ASSERT_TRUE(ret.isOk());
// Wait for listener called after registration, shouldn't timeout
- std::unique_lock<std::mutex> lock(mMutex);
EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
}
@@ -111,6 +113,7 @@
TEST_P(IThermalListenerTest, TestListener) {
int level = GetParam();
+ // Lock mutex for operation, so listener will only be processed after wait_for is called
std::unique_lock<std::mutex> lock(mMutex);
// Set the override thermal status
setThermalOverride(level);
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index e355594..2810bff 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -145,13 +145,7 @@
convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
- if(sensor.resolution == 0) {
- // Don't crash here or the device will go into a crashloop.
- ALOGW("%s must have a non-zero resolution", sensor.name);
- // For simple algos, map their resolution to 1 if it's not specified
- sensor.resolution =
- SensorDeviceUtils::defaultResolutionForType(sensor.type);
- }
+ sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
// Some sensors don't have a default resolution and will be left at 0.
// Don't crash in this case since CTS will verify that devices don't go to
@@ -165,6 +159,9 @@
SensorDeviceUtils::quantizeValue(
&sensor.maxRange, promotedResolution);
}
+ } else {
+ // Don't crash here or the device will go into a crashloop.
+ ALOGW("%s should have a non-zero resolution", sensor.name);
}
}
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index 52213cf..5aa283e 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -31,7 +31,6 @@
namespace SensorDeviceUtils {
void quantizeSensorEventValues(sensors_event_t *event, float resolution) {
- LOG_FATAL_IF(resolution == 0, "Resolution must be specified for all sensors!");
if (resolution == 0) {
return;
}
@@ -79,8 +78,26 @@
}
}
-float defaultResolutionForType(int type) {
- switch ((SensorTypeV2_1)type) {
+float resolutionForSensor(const sensor_t &sensor) {
+ switch ((SensorTypeV2_1)sensor.type) {
+ case SensorTypeV2_1::ACCELEROMETER:
+ case SensorTypeV2_1::MAGNETIC_FIELD:
+ case SensorTypeV2_1::GYROSCOPE:
+ case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
+ case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
+ case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED: {
+ if (sensor.maxRange == 0) {
+ ALOGE("No max range for sensor type %d, can't determine appropriate resolution",
+ sensor.type);
+ return sensor.resolution;
+ }
+ // Accel, gyro, and mag shouldn't have more than 24 bits of resolution on the most
+ // advanced devices.
+ double lowerBound = 2.0 * sensor.maxRange / std::pow(2, 24);
+
+ // No need to check the upper bound as that's already enforced through CTS.
+ return std::max(sensor.resolution, static_cast<float>(lowerBound));
+ }
case SensorTypeV2_1::SIGNIFICANT_MOTION:
case SensorTypeV2_1::STEP_DETECTOR:
case SensorTypeV2_1::STEP_COUNTER:
@@ -91,12 +108,14 @@
case SensorTypeV2_1::WRIST_TILT_GESTURE:
case SensorTypeV2_1::STATIONARY_DETECT:
case SensorTypeV2_1::MOTION_DETECT:
+ // Ignore input resolution as all of these sensors are required to have a resolution of
+ // 1.
return 1.0f;
default:
- // fall through and return 0 for all other types
+ // fall through and return the current resolution for all other types
break;
}
- return 0.0f;
+ return sensor.resolution;
}
HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter() {
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index c232f0b..1309971 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -19,6 +19,7 @@
#include <android/hidl/manager/1.0/IServiceNotification.h>
#include <hardware/sensors.h>
+#include <utils/Log.h>
#include <cmath>
#include <condition_variable>
@@ -33,6 +34,10 @@
// Quantizes a single value using a sensor's resolution.
inline void quantizeValue(float *value, double resolution) {
+ if (resolution == 0) {
+ return;
+ }
+
// Increase the value of the sensor's nominal resolution to ensure that
// sensor accuracy improvements, like runtime calibration, are not masked
// during requantization.
@@ -43,8 +48,8 @@
// Ensures a sensor event doesn't provide values finer grained than its sensor resolution allows.
void quantizeSensorEventValues(sensors_event_t *event, float resolution);
-// Provides a default resolution for simple sensor types if one wasn't provided by the HAL.
-float defaultResolutionForType(int type);
+// Returns the expected resolution value for the given sensor
+float resolutionForSensor(const sensor_t &sensor);
class HidlServiceRegistrationWaiter : public IServiceNotification {
public:
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index b4b5f98..3cccaf9 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <log/log.h>
#include <sys/socket.h>
#include <utils/threads.h>
@@ -28,6 +29,12 @@
#define UNUSED(x) (void)(x)
namespace android {
+namespace {
+
+// Used as the default value for the target SDK until it's obtained via getTargetSdkVersion.
+constexpr int kTargetSdkUnknown = 0;
+
+} // namespace
SensorService::SensorEventConnection::SensorEventConnection(
const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
@@ -35,9 +42,9 @@
: mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
- mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) {
+ mPackageName(packageName), mOpPackageName(opPackageName), mTargetSdk(kTargetSdkUnknown),
+ mDestroyed(false) {
mChannel = new BitTube(mService->mSocketBufferSize);
- mTargetSdk = SensorService::getTargetSdkVersion(opPackageName);
#if DEBUG_CONNECTIONS
mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
mTotalAcksNeeded = mTotalAcksReceived = 0;
@@ -47,20 +54,13 @@
SensorService::SensorEventConnection::~SensorEventConnection() {
ALOGD_IF(DEBUG_CONNECTIONS, "~SensorEventConnection(%p)", this);
destroy();
-}
-
-void SensorService::SensorEventConnection::destroy() {
- Mutex::Autolock _l(mDestroyLock);
-
- // destroy once only
- if (mDestroyed) {
- return;
- }
-
mService->cleanupConnection(this);
if (mEventCache != nullptr) {
delete[] mEventCache;
}
+}
+
+void SensorService::SensorEventConnection::destroy() {
mDestroyed = true;
}
@@ -445,6 +445,14 @@
bool success = true;
const auto iter = mHandleToAppOp.find(event.sensor);
if (iter != mHandleToAppOp.end()) {
+ if (mTargetSdk == kTargetSdkUnknown) {
+ // getTargetSdkVersion returns -1 if it fails so this operation should only be run once
+ // per connection and then cached. Perform this here as opposed to in the constructor to
+ // avoid log spam for NDK/VNDK clients that don't use sensors guarded with permissions
+ // and pass in invalid op package names.
+ mTargetSdk = SensorService::getTargetSdkVersion(mOpPackageName);
+ }
+
// Special handling for step count/detect backwards compatibility: if the app's target SDK
// is pre-Q, still permit delivering events to the app even if permission isn't granted
// (since this permission was only introduced in Q)
@@ -665,6 +673,11 @@
int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,
int reservedFlags)
{
+ if (mDestroyed) {
+ android_errorWriteLog(0x534e4554, "168211968");
+ return DEAD_OBJECT;
+ }
+
status_t err;
if (enabled) {
err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
@@ -679,10 +692,19 @@
status_t SensorService::SensorEventConnection::setEventRate(
int handle, nsecs_t samplingPeriodNs)
{
+ if (mDestroyed) {
+ android_errorWriteLog(0x534e4554, "168211968");
+ return DEAD_OBJECT;
+ }
+
return mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
}
status_t SensorService::SensorEventConnection::flush() {
+ if (mDestroyed) {
+ return DEAD_OBJECT;
+ }
+
return mService->flushSensor(this, mOpPackageName);
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 8f2d5db..9487a39 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SENSOR_EVENT_CONNECTION_H
#define ANDROID_SENSOR_EVENT_CONNECTION_H
+#include <atomic>
#include <stdint.h>
#include <sys/types.h>
#include <unordered_map>
@@ -182,8 +183,8 @@
int mTotalAcksNeeded, mTotalAcksReceived;
#endif
- mutable Mutex mDestroyLock;
- bool mDestroyed;
+ // Used to track if this object was inappropriately used after destroy().
+ std::atomic_bool mDestroyed;
// Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
// valid mapping for sensors that require a permission in order to reduce the lookup time.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index aa0dc92..2969839 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -79,6 +79,8 @@
bool SensorService::sHmacGlobalKeyIsValid = false;
std::map<String16, int> SensorService::sPackageTargetVersion;
Mutex SensorService::sPackageTargetVersionLock;
+String16 SensorService::sSensorInterfaceDescriptorPrefix =
+ String16("android.frameworks.sensorservice@");
AppOpsManager SensorService::sAppOpsManager;
#define SENSOR_SERVICE_DIR "/data/system/sensor_service"
@@ -1850,6 +1852,13 @@
}
int SensorService::getTargetSdkVersion(const String16& opPackageName) {
+ // Don't query the SDK version for the ISensorManager descriptor as it doesn't have one. This
+ // descriptor tends to be used for VNDK clients, but can technically be set by anyone so don't
+ // give it elevated privileges.
+ if (opPackageName.startsWith(sSensorInterfaceDescriptorPrefix)) {
+ return -1;
+ }
+
Mutex::Autolock packageLock(sPackageTargetVersionLock);
int targetSdkVersion = -1;
auto entry = sPackageTargetVersion.find(opPackageName);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3bb8421..052cbfe 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -424,6 +424,7 @@
static AppOpsManager sAppOpsManager;
static std::map<String16, int> sPackageTargetVersion;
static Mutex sPackageTargetVersionLock;
+ static String16 sSensorInterfaceDescriptorPrefix;
};
} // namespace android
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index 80c3b65..ae0a984 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -58,7 +58,7 @@
std::vector<int32_t> buckets = chargeCycles.cycleBucket;
int initialSize = buckets.size();
for (int i = 0; i < 10 - initialSize; i++) {
- buckets.push_back(-1); // Push -1 for buckets that do not exist.
+ buckets.push_back(0); // Push 0 for buckets that do not exist.
}
android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b039f6e..ed0d75b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -13,14 +13,16 @@
cc_defaults {
name: "libsurfaceflinger_defaults",
- defaults: ["surfaceflinger_defaults"],
+ defaults: [
+ "surfaceflinger_defaults",
+ "skia_deps",
+ ],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
],
shared_libs: [
- "android.frameworks.vr.composer@2.0",
"android.hardware.configstore-utils",
"android.hardware.configstore@1.0",
"android.hardware.configstore@1.1",
@@ -36,7 +38,6 @@
"android.hardware.power-cpp",
"libbase",
"libbinder",
- "libbufferhubqueue",
"libcutils",
"libEGL",
"libfmq",
@@ -47,7 +48,6 @@
"liblayers_proto",
"liblog",
"libnativewindow",
- "libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
"libstatslog",
@@ -58,21 +58,13 @@
"libutils",
"libSurfaceFlingerProp",
],
- // VrComposer is not used when building surfaceflinger for vendors
- target: {
- vendor: {
- exclude_shared_libs: [
- "android.frameworks.vr.composer@2.0",
- ],
- },
- },
static_libs: [
"libcompositionengine",
+ "libframetimeline",
"libperfetto_client_experimental",
"librenderengine",
"libserviceutils",
"libtrace_proto",
- "libvrflinger",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -108,11 +100,11 @@
defaults: ["libsurfaceflinger_defaults"],
cflags: [
"-fvisibility=hidden",
- "-fwhole-program-vtables", // requires ThinLTO
],
lto: {
thin: true,
},
+ whole_program_vtables: true, // Requires ThinLTO
// TODO(b/131771163): Fix broken fuzzer support with LTO.
sanitize: {
fuzzer: false,
@@ -186,35 +178,12 @@
],
}
-cc_library_shared {
- // Please use libsurfaceflinger_defaults to configure how the sources are
- // built, so the same settings can be used elsewhere.
- name: "libsurfaceflinger",
- defaults: ["libsurfaceflinger_production_defaults"],
- srcs: [
- ":libsurfaceflinger_sources",
-
- // Note: SurfaceFlingerFactory is not in the default sources so that it
- // can be easily replaced.
- "SurfaceFlingerFactory.cpp",
- ],
- cflags: [
- "-DUSE_VR_COMPOSER=1",
- ],
- // VrComposer is not used when building surfaceflinger for vendors
- target: {
- vendor: {
- cflags: [
- "-DUSE_VR_COMPOSER=0",
- ],
- },
- },
- logtags: ["EventLog/EventLogTags.logtags"],
-}
-
cc_defaults {
name: "libsurfaceflinger_binary",
- defaults: ["surfaceflinger_defaults"],
+ defaults: [
+ "surfaceflinger_defaults",
+ "libsurfaceflinger_production_defaults",
+ ],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
],
@@ -239,23 +208,31 @@
"libserviceutils",
"libtrace_proto",
],
- ldflags: ["-Wl,--export-dynamic"],
}
filegroup {
name: "surfaceflinger_binary_sources",
- srcs: ["main_surfaceflinger.cpp"],
+ srcs: [
+ ":libsurfaceflinger_sources",
+ "main_surfaceflinger.cpp",
+ ],
}
cc_binary {
name: "surfaceflinger",
defaults: ["libsurfaceflinger_binary"],
init_rc: ["surfaceflinger.rc"],
- srcs: [":surfaceflinger_binary_sources"],
+ srcs: [
+ ":surfaceflinger_binary_sources",
+ // Note: SurfaceFlingerFactory is not in the filegroup so that it
+ // can be easily replaced.
+ "SurfaceFlingerFactory.cpp",
+ ],
shared_libs: [
- "libsurfaceflinger",
"libSurfaceFlingerProp",
],
+
+ logtags: ["EventLog/EventLogTags.logtags"],
}
subdirs = [
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index cf60b71..c77298e 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -366,7 +366,7 @@
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (!display) {
// Do nothing.
- } else if (const auto displayId = display->getId();
+ } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 2483abb..1cd753b 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -37,6 +37,7 @@
#include "BufferLayerConsumer.h"
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
+#include "FrameTimeline.h"
#include "FrameTracker.h"
#include "Layer.h"
#include "LayerVector.h"
@@ -50,10 +51,7 @@
explicit BufferLayer(const LayerCreationArgs& args);
virtual ~BufferLayer() override;
- // -----------------------------------------------------------------------
- // Overriden from Layer
- // -----------------------------------------------------------------------
-public:
+ // Implements Layer.
sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
compositionengine::LayerFECompositionState* editCompositionState() override;
@@ -118,41 +116,6 @@
ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
- // -----------------------------------------------------------------------
- // Functions that must be implemented by derived classes
- // -----------------------------------------------------------------------
-private:
- virtual bool fenceHasSignaled() const = 0;
- virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
-
- PixelFormat getPixelFormat() const;
-
- // 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]);
-
- virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
-
- virtual bool getAutoRefresh() const = 0;
- virtual bool getSidebandStreamChanged() const = 0;
-
- // Latch sideband stream and returns true if the dirty region should be updated.
- virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
-
- virtual bool hasFrameUpdate() const = 0;
-
- virtual status_t bindTextureImage() = 0;
- virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) = 0;
-
- virtual status_t updateActiveBuffer() = 0;
- virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
-
- // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
- // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
- // detection.
- bool needsInputInfo() const override { return !mPotentialCursor; }
-
protected:
struct BufferInfo {
nsecs_t mDesiredPresentTime;
@@ -204,7 +167,7 @@
void updateCloneBufferInfo() override;
uint64_t mPreviousFrameNumber = 0;
- virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
+ uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override;
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
@@ -213,6 +176,29 @@
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
private:
+ virtual bool fenceHasSignaled() const = 0;
+ virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
+ virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
+
+ virtual bool getAutoRefresh() const = 0;
+ virtual bool getSidebandStreamChanged() const = 0;
+
+ // Latch sideband stream and returns true if the dirty region should be updated.
+ virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
+
+ virtual bool hasFrameUpdate() const = 0;
+
+ virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ nsecs_t expectedPresentTime) = 0;
+
+ virtual status_t updateActiveBuffer() = 0;
+ virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+
+ // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
+ // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
+ // detection.
+ bool needsInputInfo() const override { return !mPotentialCursor; }
+
// Returns true if this layer requires filtering
bool needsFiltering(const DisplayDevice*) const override;
bool needsFilteringForScreenshots(const DisplayDevice*,
@@ -222,6 +208,12 @@
// and its parent layer is not bounded
Rect getBufferSize(const State& s) const override;
+ PixelFormat getPixelFormat() const;
+
+ // 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]);
+
std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 8722952..7c73df2 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -25,7 +25,7 @@
#include "BufferLayerConsumer.h"
#include "Layer.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
#include <inttypes.h>
@@ -153,25 +153,9 @@
if (err != NO_ERROR) {
return err;
}
-
- if (!mRE.useNativeFenceSync()) {
- // Bind the new buffer to the GL texture.
- //
- // Older devices require the "implicit" synchronization provided
- // by glEGLImageTargetTexture2DOES, which this method calls. Newer
- // devices will either call this in Layer::onDraw, or (if it's not
- // a GL-composited layer) not at all.
- err = bindTextureImageLocked();
- }
-
return err;
}
-status_t BufferLayerConsumer::bindTextureImage() {
- Mutex::Autolock lock(mMutex);
- return bindTextureImageLocked();
-}
-
void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
if (!fence->isValid()) {
return;
@@ -292,17 +276,6 @@
return err;
}
-status_t BufferLayerConsumer::bindTextureImageLocked() {
- ATRACE_CALL();
-
- if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) {
- return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(),
- mCurrentFence);
- }
-
- return NO_INIT;
-}
-
void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
Mutex::Autolock lock(mMutex);
memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 5e3044f..dd39214 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -34,7 +34,6 @@
namespace android {
// ----------------------------------------------------------------------------
-class DispSync;
class Layer;
class String8;
@@ -95,9 +94,6 @@
status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
- // See BufferLayerConsumer::bindTextureImageLocked().
- status_t bindTextureImage();
-
// setReleaseFence stores a fence that will signal when the current buffer
// is no longer being read. This fence will be returned to the producer
// when the current buffer is released by updateTexImage(). Multiple
@@ -215,10 +211,6 @@
PendingRelease* pendingRelease = nullptr)
EXCLUDES(mImagesMutex);
- // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
- // If the bind succeeds, this calls doFenceWait.
- status_t bindTextureImageLocked();
-
private:
// Utility class for managing GraphicBuffer references into renderengine
class Image {
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 89284f2..0863a22 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
#include "TimeStats/TimeStats.h"
namespace android {
+using PresentState = frametimeline::SurfaceFrame::PresentState;
BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
@@ -109,7 +110,7 @@
Mutex::Autolock lock(mQueueItemLock);
- const int64_t addedTime = mQueueItems[0].mTimestamp;
+ const int64_t addedTime = mQueueItems[0].item.mTimestamp;
// Ignore timestamps more than a second in the future
const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
@@ -136,7 +137,7 @@
}
Mutex::Autolock lock(mQueueItemLock);
- if (mQueueItems[0].mIsDroppable) {
+ if (mQueueItems[0].item.mIsDroppable) {
// Even though this buffer's fence may not have signaled yet, it could
// be replaced by another buffer before it has a chance to, which means
// that it's possible to get into a situation where a buffer is never
@@ -144,7 +145,7 @@
return true;
}
const bool fenceSignaled =
- mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
TimeStats::LatchSkipReason::LateAcquire);
@@ -159,12 +160,12 @@
}
Mutex::Autolock lock(mQueueItemLock);
- return mQueueItems[0].mTimestamp <= expectedPresentTime;
+ return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
}
uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
Mutex::Autolock lock(mQueueItemLock);
- uint64_t frameNumber = mQueueItems[0].mFrameNumber;
+ uint64_t frameNumber = mQueueItems[0].item.mFrameNumber;
// The head of the queue will be dropped if there are signaled and timely frames behind it
if (isRemovedFromCurrentState()) {
@@ -173,23 +174,23 @@
for (int i = 1; i < mQueueItems.size(); i++) {
const bool fenceSignaled =
- mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
break;
}
// We don't drop frames without explicit timestamps
- if (mQueueItems[i].mIsAutoTimestamp) {
+ if (mQueueItems[i].item.mIsAutoTimestamp) {
break;
}
- const nsecs_t desiredPresent = mQueueItems[i].mTimestamp;
+ const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp;
if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
desiredPresent > expectedPresentTime) {
break;
}
- frameNumber = mQueueItems[i].mFrameNumber;
+ frameNumber = mQueueItems[i].item.mFrameNumber;
}
return frameNumber;
@@ -229,10 +230,6 @@
return mQueuedFrames > 0;
}
-status_t BufferQueueLayer::bindTextureImage() {
- return mConsumer->bindTextureImage();
-}
-
status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
// This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -258,11 +255,11 @@
Mutex::Autolock lock(mQueueItemLock);
for (int i = 0; i < mQueueItems.size(); i++) {
bool fenceSignaled =
- mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
break;
}
- lastSignaledFrameNumber = mQueueItems[i].mFrameNumber;
+ lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber;
}
}
const uint64_t maxFrameNumberToAcquire =
@@ -280,9 +277,13 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
- mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
- mQueueItems.removeAt(0);
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+ if (mQueueItems[0].surfaceFrame) {
+ mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+ PresentState::Dropped);
+ }
+ mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
}
return BAD_VALUE;
@@ -293,6 +294,12 @@
// early.
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
+ for (auto& [item, surfaceFrame] : mQueueItems) {
+ if (surfaceFrame) {
+ mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ PresentState::Dropped);
+ }
+ }
mQueueItems.clear();
mQueuedFrames = 0;
mFlinger->mTimeStats->onDestroy(layerId);
@@ -316,19 +323,29 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
- while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
- mQueueItems.removeAt(0);
+ while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+ if (mQueueItems[0].surfaceFrame) {
+ mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+ PresentState::Dropped);
+ }
+ mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
}
- uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
+ uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId();
mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
- mQueueItems.removeAt(0);
+ if (mQueueItems[0].surfaceFrame) {
+ mQueueItems[0].surfaceFrame->setAcquireFenceTime(
+ mQueueItems[0].item.mFenceTime->getSignalTime());
+ mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+ PresentState::Presented);
+ }
+ mQueueItems.erase(mQueueItems.begin());
}
// Decrement the queued-frames count. Signal another event if we
@@ -364,6 +381,10 @@
return NO_ERROR;
}
+void BufferQueueLayer::setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) {
+ mFrameTimelineVsyncId = frameTimelineVsyncId;
+}
+
// -----------------------------------------------------------------------
// Interface implementation for BufferLayerConsumer::ContentsChangedListener
// -----------------------------------------------------------------------
@@ -420,7 +441,11 @@
}
}
- mQueueItems.push_back(item);
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+ surfaceFrame->setActualQueueTime(systemTime());
+
+ mQueueItems.push_back({item, std::move(surfaceFrame)});
mQueuedFrames++;
// Wake up any pending callbacks
@@ -453,7 +478,12 @@
ALOGE("Can't replace a frame on an empty queue");
return;
}
- mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
+
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+ surfaceFrame->setActualQueueTime(systemTime());
+ mQueueItems[mQueueItems.size() - 1].item = item;
+ mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
// Wake up any pending callbacks
mLastFrameNumberReceived = item.mFrameNumber;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5ebc22d..fb8a0c2 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -22,6 +22,10 @@
namespace android {
+namespace frametimeline {
+class SurfaceFrame;
+}
+
/*
* A new BufferQueue and a new BufferLayerConsumer are created when the
* BufferLayer is first referenced.
@@ -35,10 +39,7 @@
explicit BufferQueueLayer(const LayerCreationArgs&);
~BufferQueueLayer() override;
- // -----------------------------------------------------------------------
- // Interface implementation for Layer
- // -----------------------------------------------------------------------
-public:
+ // Implements Layer.
const char* getType() const override { return "BufferQueueLayer"; }
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -54,41 +55,12 @@
bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
- // -----------------------------------------------------------------------
-
- // -----------------------------------------------------------------------
- // Interface implementation for BufferLayer
- // -----------------------------------------------------------------------
-public:
+ // Implements BufferLayer.
bool fenceHasSignaled() const override;
bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
-private:
- uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
-
- bool getAutoRefresh() const override;
- bool getSidebandStreamChanged() const override;
-
- bool latchSidebandStream(bool& recomputeVisibleRegions) override;
- void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
-
- bool hasFrameUpdate() const override;
-
- status_t bindTextureImage() override;
- status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
- nsecs_t expectedPresentTime) override;
-
- status_t updateActiveBuffer() override;
- status_t updateFrameNumber(nsecs_t latchTime) override;
-
- sp<Layer> createClone() override;
-
- void onFrameAvailable(const BufferItem& item);
- void onFrameReplaced(const BufferItem& item);
- void onSidebandStreamChanged();
- void onFrameDequeued(const uint64_t bufferId);
- void onFrameDetached(const uint64_t bufferId);
- void onFrameCancelled(const uint64_t bufferId);
+ status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
+ sp<IGraphicBufferProducer> getProducer() const;
protected:
void gatherBufferInfo() override;
@@ -114,19 +86,39 @@
BufferQueueLayer* mBufferQueueLayer = nullptr;
Mutex mMutex;
};
- // -----------------------------------------------------------------------
-
-public:
- status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
-
- sp<IGraphicBufferProducer> getProducer() const;
private:
- // Temporary - Used only for LEGACY camera mode.
- uint32_t getProducerStickyTransform() const;
+ uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
+
+ bool getAutoRefresh() const override;
+ bool getSidebandStreamChanged() const override;
+
+ bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+ void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
+ bool hasFrameUpdate() const override;
+
+ status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+ nsecs_t expectedPresentTime) override;
+
+ status_t updateActiveBuffer() override;
+ status_t updateFrameNumber(nsecs_t latchTime) override;
+ void setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) override;
+
+ sp<Layer> createClone() override;
void onFirstRef() override;
+ void onFrameAvailable(const BufferItem& item);
+ void onFrameReplaced(const BufferItem& item);
+ void onSidebandStreamChanged();
+ void onFrameDequeued(const uint64_t bufferId);
+ void onFrameDetached(const uint64_t bufferId);
+ void onFrameCancelled(const uint64_t bufferId);
+
+ // Temporary - Used only for LEGACY camera mode.
+ uint32_t getProducerStickyTransform() const;
+
sp<BufferLayerConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
@@ -138,7 +130,14 @@
// Local copy of the queued contents of the incoming BufferQueue
mutable Mutex mQueueItemLock;
Condition mQueueItemCondition;
- Vector<BufferItem> mQueueItems;
+
+ struct BufferData {
+ BufferData(BufferItem item, std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame)
+ : item(item), surfaceFrame(std::move(surfaceFrame)) {}
+ BufferItem item;
+ std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame;
+ };
+ std::vector<BufferData> mQueueItems;
std::atomic<uint64_t> mLastFrameNumberReceived{0};
bool mAutoRefresh{false};
@@ -148,6 +147,11 @@
std::atomic<bool> mSidebandStreamChanged{false};
sp<ContentsChangedListener> mContentsChangedListener;
+
+ // The last vsync id received on this layer. This will be used when we get
+ // a buffer to correlate the buffer with the vsync id. Can only be accessed
+ // with the SF state lock held.
+ std::optional<int64_t> mFrameTimelineVsyncId;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 884cc0c..361c1f3 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -161,6 +161,10 @@
bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
mCurrentStateModified = mCurrentState.modified;
bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
+ if (stateUpdateAvailable && mCallbackHandleAcquireTime != -1) {
+ // Update the acquire fence time if we have a buffer
+ mSurfaceFrame->setAcquireFenceTime(mCallbackHandleAcquireTime);
+ }
mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
mCurrentState.modified = false;
return stateUpdateAvailable;
@@ -258,12 +262,12 @@
bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
nsecs_t postTime, nsecs_t desiredPresentTime,
- const client_cache_t& clientCacheId) {
+ const client_cache_t& clientCacheId, uint64_t frameNumber) {
if (mCurrentState.buffer) {
mReleasePreviousBuffer = true;
}
- mCurrentState.frameNumber++;
+ mCurrentState.frameNumber = frameNumber;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
@@ -508,13 +512,6 @@
return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-status_t BufferStateLayer::bindTextureImage() {
- const State& s(getDrawingState());
- auto& engine(mFlinger->getRenderEngine());
-
- return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
-}
-
status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
nsecs_t /*expectedPresentTime*/) {
const State& s(getDrawingState());
@@ -559,20 +556,6 @@
handle->frameNumber = mDrawingState.frameNumber;
}
- if (!SyncFeatures::getInstance().useNativeFenceSync()) {
- // Bind the new buffer to the GL texture.
- //
- // Older devices require the "implicit" synchronization provided
- // by glEGLImageTargetTexture2DOES, which this method calls. Newer
- // devices will either call this in Layer::onDraw, or (if it's not
- // a GL-composited layer) not at all.
- status_t err = bindTextureImage();
- if (err != NO_ERROR) {
- mFlinger->mTimeStats->onDestroy(layerId);
- return BAD_VALUE;
- }
- }
-
mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
std::make_shared<FenceTime>(mDrawingState.acquireFence));
mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 00fa7f7..c13f5e8 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -36,9 +36,7 @@
~BufferStateLayer() override;
- // -----------------------------------------------------------------------
- // Interface implementation for Layer
- // -----------------------------------------------------------------------
+ // Implements Layer.
const char* getType() const override { return "BufferStateLayer"; }
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
@@ -72,7 +70,8 @@
bool setCrop(const Rect& crop) override;
bool setFrame(const Rect& frame) override;
bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
- nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
+ nsecs_t desiredPresentTime, const client_cache_t& clientCacheId,
+ uint64_t frameNumber) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -117,6 +116,8 @@
uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
private:
+ friend class SlotGenerationTest;
+
bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime);
@@ -129,7 +130,6 @@
bool hasFrameUpdate() const override;
- status_t bindTextureImage() override;
status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) override;
@@ -141,8 +141,6 @@
// Crop that applies to the buffer
Rect computeCrop(const State& s);
-private:
- friend class SlotGenerationTest;
bool willPresentCurrentTransaction() const;
static const std::array<float, 16> IDENTITY_MATRIX;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 78bbcba..aac6c91 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -79,17 +79,18 @@
status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parentHandle,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
+ uint32_t* outTransformHint) {
// We rely on createLayer to check permissions.
return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- parentHandle, nullptr, outTransformHint);
+ parentHandle, outLayerId, nullptr, outTransformHint);
}
status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint) {
if (mFlinger->authenticateSurfaceTexture(parent) == false) {
ALOGE("failed to authenticate surface texture");
@@ -103,11 +104,12 @@
}
return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- nullptr, layer, outTransformHint);
+ nullptr, outLayerId, layer, outTransformHint);
}
-status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
- return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle);
+status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) {
+ return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
}
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index e9063e5..15cd763 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -28,13 +28,9 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Layer;
class SurfaceFlinger;
-// ---------------------------------------------------------------------------
-
class Client : public BnSurfaceComposerClient
{
public:
@@ -54,17 +50,18 @@
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint = nullptr);
virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
const sp<IGraphicBufferProducer>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+ sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
uint32_t* outTransformHint = nullptr);
- status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
+ status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle,
+ int32_t* outLayerId);
virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
@@ -80,7 +77,6 @@
mutable Mutex mLock;
};
-// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SF_CLIENT_H
diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h
index b7d61ce..b0cc030 100644
--- a/services/surfaceflinger/Colorizer.h
+++ b/services/surfaceflinger/Colorizer.h
@@ -21,8 +21,6 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Colorizer {
bool mEnabled;
public:
@@ -59,9 +57,6 @@
}
};
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
+} // namespace android
#endif /* ANDROID_SURFACE_FLINGER_COLORIZER_H */
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index b3b9fe5..57dc60b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -6,7 +6,6 @@
"-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
],
shared_libs: [
- "android.frameworks.vr.composer@2.0",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
@@ -96,8 +95,9 @@
"tests/MockHWC2.cpp",
"tests/MockHWComposer.cpp",
"tests/MockPowerAdvisor.cpp",
- "tests/OutputTest.cpp",
"tests/OutputLayerTest.cpp",
+ "tests/OutputTest.cpp",
+ "tests/ProjectionSpaceTest.cpp",
"tests/RenderSurfaceTest.cpp",
],
static_libs: [
@@ -121,13 +121,6 @@
//
// You can either "make dist tests" before flashing, or set this
// option to false temporarily.
-
-
- // FIXME: ASAN build is broken for a while, but was not discovered
- // since new PM silently suppressed ASAN. Temporarily turn off ASAN
- // to unblock the compiler upgrade process.
- // address: true,
- // http://b/139747256
- address: false,
+ address: true,
},
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index a38d1f3..01dd534 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -34,8 +34,8 @@
*/
class Display : public virtual Output {
public:
- // Gets the HWC DisplayId for the display if there is one
- virtual const std::optional<DisplayId>& getId() const = 0;
+ // Gets the DisplayId for the display
+ virtual DisplayId getId() const = 0;
// True if the display is secure
virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6bc677d..95ba9f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -20,12 +20,14 @@
#include <optional>
#include <string>
+#include <ui/DisplayId.h>
#include <ui/DisplayInfo.h>
#include <ui/PixelFormat.h>
#include <ui/Size.h>
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
namespace android::compositionengine {
@@ -65,6 +67,9 @@
// Debugging. Human readable name for the display.
std::string name;
+
+ // Generator for IDs of virtual displays, which are backed by the GPU.
+ DisplayIdGenerator<GpuVirtualDisplayId>* gpuVirtualDisplayIdGenerator;
};
/**
@@ -95,6 +100,12 @@
return *this;
}
+ DisplayCreationArgsBuilder& setGpuVirtualDisplayIdGenerator(
+ DisplayIdGenerator<GpuVirtualDisplayId>& generator) {
+ mArgs.gpuVirtualDisplayIdGenerator = &generator;
+ return *this;
+ }
+
DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
mArgs.isSecure = isSecure;
return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a9763b..77400eb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -116,7 +116,7 @@
FloatRect geomLayerBounds;
// length of the shadow in screen space
- float shadowRadius;
+ float shadowRadius{0.f};
/*
* Geometry state
@@ -130,16 +130,6 @@
Rect geomContentCrop;
Rect geomCrop;
- /*
- * Extra metadata
- */
-
- // The type for this layer
- int type{0};
-
- // The appId for this layer
- int appId{0};
-
GenericLayerMetadataMap metadata;
/*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 6cc7a53..d590f23 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,12 +163,10 @@
virtual void setCompositionEnabled(bool) = 0;
// Sets the projection state to use
- virtual void setProjection(const ui::Transform&, uint32_t orientation,
- const Rect& orientedDisplaySpaceRect,
- const Rect& layerStackSpaceRect, const Rect& displaySpaceRect,
- bool needsFiltering) = 0;
+ virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+ const Rect& orientedDisplaySpaceRect) = 0;
// Sets the bounds to use
- virtual void setDisplaySpaceSize(const ui::Size&) = 0;
+ virtual void setDisplaySize(const ui::Size&) = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 9d15665..7ca91d8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -38,14 +38,73 @@
// Rect onto which content is projected.
Rect content;
+
+ // The orientation of this space. This value is meaningful only in relation to the rotation
+ // of another projection space and it's used to determine the rotating transformation when
+ // mapping between the two.
+ // As a convention when using this struct orientation = 0 for the "oriented*" projection
+ // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+ // of the display space will become 90, while the orientation of the layer stack space will
+ // remain the same.
+ ui::Rotation orientation = ui::ROTATION_0;
+
+ // Returns a transform which maps this.content into destination.content
+ // and also rotates according to this.orientation and destination.orientation
+ ui::Transform getTransform(const ProjectionSpace& destination) const {
+ ui::Rotation rotation = destination.orientation - orientation;
+
+ // Compute a transformation which rotates the destination in a way it has the same
+ // orientation as us.
+ const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
+ ui::Transform inverseRotatingTransform;
+ inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
+ destination.bounds.height());
+ // The destination content rotated so it has the same orientation as us.
+ Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+
+ // Compute translation from the source content to (0, 0).
+ const float sourceX = content.left;
+ const float sourceY = content.top;
+ ui::Transform sourceTranslation;
+ sourceTranslation.set(-sourceX, -sourceY);
+
+ // Compute scaling transform which maps source content to destination content, assuming
+ // they are both at (0, 0).
+ ui::Transform scale;
+ const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
+ const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+ scale.set(scaleX, 0, 0, scaleY);
+
+ // Compute translation from (0, 0) to the orientated destination content.
+ const float destX = orientedDestContent.left;
+ const float destY = orientedDestContent.top;
+ ui::Transform destTranslation;
+ destTranslation.set(destX, destY);
+
+ // Compute rotation transform.
+ const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
+ auto orientedDestWidth = destination.bounds.width();
+ auto orientedDestHeight = destination.bounds.height();
+ if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+ std::swap(orientedDestWidth, orientedDestHeight);
+ }
+ ui::Transform rotationTransform;
+ rotationTransform.set(orientationFlags, orientedDestWidth, orientedDestHeight);
+
+ // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
+ // Apply the logical translation, scale to physical size, apply the
+ // physical translation and finally rotate to the physical orientation.
+ return rotationTransform * destTranslation * scale * sourceTranslation;
+ }
};
} // namespace compositionengine
inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
- return android::base::StringPrintf("ProjectionSpace(bounds = %s, content = %s)",
- to_string(space.bounds).c_str(),
- to_string(space.content).c_str());
+ return android::base::
+ StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
+ to_string(space.bounds).c_str(), to_string(space.content).c_str(),
+ toCString(space.orientation));
}
// Defining PrintTo helps with Google Tests.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 7a4f738..54e91ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -57,7 +57,7 @@
void finishFrame(const CompositionRefreshArgs&) override;
// compositionengine::Display overrides
- const std::optional<DisplayId>& getId() const override;
+ DisplayId getId() const override;
bool isSecure() const override;
bool isVirtual() const override;
void disconnect() override;
@@ -85,12 +85,14 @@
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
// Testing
- void setDisplayIdForTesting(std::optional<DisplayId> displayId);
+ void setDisplayIdForTesting(DisplayId displayId);
private:
bool mIsVirtual = false;
- std::optional<DisplayId> mId;
+ bool mIsDisconnected = false;
+ DisplayId mId;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+ DisplayIdGenerator<GpuVirtualDisplayId>* mGpuVirtualDisplayIdGenerator;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 57b7a97..5b832a5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,10 +38,9 @@
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
- void setProjection(const ui::Transform&, uint32_t orientation,
- const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
- const Rect& displaySpaceRect, bool needsFiltering) override;
- void setDisplaySpaceSize(const ui::Size&) override;
+ void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+ const Rect& orientedDisplaySpaceRect) override;
+ void setDisplaySize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 462d952..06e6a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -39,7 +39,7 @@
namespace compositionengine::impl {
struct OutputCompositionState {
- // If false, composition will not per performed for this display
+ // If false, composition will not be performed for this display
bool isEnabled{false};
// If false, this output is not considered secure
@@ -64,15 +64,18 @@
uint32_t layerStackId{~0u};
// The common space for all layers in the layer stack. layerStackSpace.content is the Rect
- // which gets projected on the display. The content in this space is always in a single
- // orientation.
+ // which gets projected on the display. The orientation of this space is always ROTATION_0.
ProjectionSpace layerStackSpace;
// Oriented physical display space. It will have the same size as displaySpace oriented to
- // match the orientation of layerStackSpace. The content in this space is always in a single
- // orientation.
+ // match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0.
ProjectionSpace orientedDisplaySpace;
+ // The space of the framebuffer. Its bounds match the size of the framebuffer and its
+ // orientation matches the orientation of the display. Typically the framebuffer space will
+ // be identical to the physical display space.
+ ProjectionSpace framebufferSpace;
+
// The space of the physical display. It is as big as the currently active display mode. The
// content in this space can be rotated.
ProjectionSpace displaySpace;
@@ -80,9 +83,6 @@
// Transformation from layerStackSpace to displaySpace
ui::Transform transform;
- // The physical orientation of the display, expressed as ui::Transform orientation flags.
- uint32_t orientation{0};
-
// If true, RenderEngine filtering should be enabled
bool needsFiltering{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 75394fa..d2b38d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -110,7 +110,7 @@
void dump(std::string& result) const;
// Timestamp for when the layer is queued for client composition
- nsecs_t clientCompositionTimestamp;
+ nsecs_t clientCompositionTimestamp{0};
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 3a4c70f..08a8b84 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -32,7 +32,7 @@
Display();
virtual ~Display();
- MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+ MOCK_CONST_METHOD0(getId, DisplayId());
MOCK_CONST_METHOD0(isSecure, bool());
MOCK_CONST_METHOD0(isVirtual, bool());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 375d334..d6fbd7f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,9 +36,8 @@
MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
- MOCK_METHOD6(setProjection,
- void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
- MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
+ MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
+ MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index d201104..0b0b8d5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -51,18 +51,26 @@
void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
mIsVirtual = !args.physical;
- mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
mPowerAdvisor = args.powerAdvisor;
-
editState().isSecure = args.isSecure;
-
+ editState().displaySpace.bounds = Rect(args.pixels);
setLayerStackFilter(args.layerStackId,
- args.physical ? args.physical->type == DisplayConnectionType::Internal
- : false);
+ args.physical && args.physical->type == DisplayConnectionType::Internal);
setName(args.name);
+ mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator;
- if (!args.physical && args.useHwcVirtualDisplays) {
- mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+ if (args.physical) {
+ mId = args.physical->id;
+ } else {
+ std::optional<DisplayId> id;
+ if (args.useHwcVirtualDisplays) {
+ id = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+ }
+ if (!id) {
+ id = mGpuVirtualDisplayIdGenerator->nextId();
+ }
+ LOG_ALWAYS_FATAL_IF(!id, "Failed to generate display ID");
+ mId = *id;
}
}
@@ -77,7 +85,7 @@
return Output::isValid() && mPowerAdvisor;
}
-const std::optional<DisplayId>& Display::getId() const {
+DisplayId Display::getId() const {
return mId;
}
@@ -93,31 +101,36 @@
return mId;
}
-void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+void Display::setDisplayIdForTesting(DisplayId displayId) {
mId = displayId;
}
void Display::disconnect() {
- if (!mId) {
+ if (mIsDisconnected) {
return;
}
- auto& hwc = getCompositionEngine().getHwComposer();
- hwc.disconnectDisplay(*mId);
- mId.reset();
+ mIsDisconnected = true;
+ if (const auto id = GpuVirtualDisplayId::tryCast(mId)) {
+ mGpuVirtualDisplayIdGenerator->markUnused(*id);
+ return;
+ }
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ LOG_FATAL_IF(!halDisplayId);
+ getCompositionEngine().getHwComposer().disconnectDisplay(*halDisplayId);
}
void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
Output::setColorTransform(args);
-
- if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
return;
}
auto& hwc = getCompositionEngine().getHwComposer();
- status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
+ status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
- mId ? to_string(*mId).c_str() : "", result);
+ to_string(mId).c_str(), result);
}
void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -139,8 +152,10 @@
Output::setColorProfile(colorProfile);
- auto& hwc = getCompositionEngine().getHwComposer();
- hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent);
+ const auto physicalId = PhysicalDisplayId::tryCast(mId);
+ LOG_FATAL_IF(!physicalId);
+ getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
+ colorProfile.renderIntent);
}
void Display::dump(std::string& out) const {
@@ -149,14 +164,8 @@
StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str());
out.append("\n ");
-
dumpVal(out, "isVirtual", mIsVirtual);
- if (mId) {
- dumpVal(out, "hwcId", to_string(*mId));
- } else {
- StringAppendF(&out, "no hwcId, ");
- }
-
+ dumpVal(out, "DisplayId", to_string(mId));
out.append("\n");
Output::dumpBase(out);
@@ -177,31 +186,33 @@
std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
const sp<compositionengine::LayerFE>& layerFE) const {
- auto result = impl::createOutputLayer(*this, layerFE);
+ auto outputLayer = impl::createOutputLayer(*this, layerFE);
- if (result && mId) {
+ if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+ outputLayer && !mIsDisconnected && halDisplayId) {
auto& hwc = getCompositionEngine().getHwComposer();
- auto displayId = *mId;
// Note: For the moment we ensure it is safe to take a reference to the
// HWComposer implementation by destroying all the OutputLayers (and
// hence the HWC2::Layers they own) before setting a new HWComposer. See
// for example SurfaceFlinger::updateVrFlinger().
// TODO(b/121291683): Make this safer.
- auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),
- [&hwc, displayId](HWC2::Layer* layer) {
- hwc.destroyLayer(displayId, layer);
- });
+ auto hwcLayer =
+ std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId),
+ [&hwc, id = *halDisplayId](HWC2::Layer* layer) {
+ hwc.destroyLayer(id, layer);
+ });
ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
getName().c_str());
- result->setHwcLayer(std::move(hwcLayer));
+ outputLayer->setHwcLayer(std::move(hwcLayer));
}
- return result;
+ return outputLayer;
}
void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
Output::setReleasedLayers(refreshArgs);
- if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+ if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
+ refreshArgs.layersWithQueuedFrames.empty()) {
return;
}
@@ -237,19 +248,25 @@
ATRACE_CALL();
ALOGV(__FUNCTION__);
+ if (mIsDisconnected) {
+ return;
+ }
+
// Default to the base settings -- client composition only.
Output::chooseCompositionStrategy();
- // If we don't have a HWC display, then we are done
- if (!mId) {
+ // If we don't have a HWC display, then we are done.
+ const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (!halDisplayId) {
return;
}
// Get any composition changes requested by the HWC device, and apply them.
std::optional<android::HWComposer::DeviceRequestedChanges> changes;
auto& hwc = getCompositionEngine().getHwComposer();
- if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),
- &changes);
+ if (status_t result =
+ hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+ &changes);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -270,8 +287,12 @@
bool Display::getSkipColorTransform() const {
const auto& hwc = getCompositionEngine().getHwComposer();
- return mId ? hwc.hasDisplayCapability(*mId, hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM)
- : hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
+ if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ return hwc.hasDisplayCapability(*halDisplayId,
+ hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+ }
+
+ return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
}
bool Display::anyLayersRequireClientComposition() const {
@@ -338,16 +359,17 @@
}
compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
- auto result = impl::Output::presentAndGetFrameFences();
+ auto fences = impl::Output::presentAndGetFrameFences();
- if (!mId) {
- return result;
+ const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+ if (mIsDisconnected || !halDisplayIdOpt) {
+ return fences;
}
auto& hwc = getCompositionEngine().getHwComposer();
- hwc.presentAndGetReleaseFences(*mId);
+ hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
- result.presentFence = hwc.getPresentFence(*mId);
+ fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
// TODO(b/121291683): Change HWComposer call to return entire map
for (const auto* layer : getOutputLayersOrderedByZ()) {
@@ -356,19 +378,19 @@
continue;
}
- result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer));
+ fences.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*halDisplayIdOpt, hwcLayer));
}
- hwc.clearReleaseFences(*mId);
+ hwc.clearReleaseFences(*halDisplayIdOpt);
- return result;
+ return fences;
}
void Display::setExpensiveRenderingExpected(bool enabled) {
Output::setExpensiveRenderingExpected(enabled);
- if (mPowerAdvisor && mId) {
- mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled);
+ if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
+ mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
}
}
@@ -377,11 +399,10 @@
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- if (!mId) {
- if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
- ALOGV("Skipping display composition");
- return;
- }
+ if (GpuVirtualDisplayId::tryCast(mId) &&
+ getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+ ALOGV("Skipping display composition");
+ return;
}
impl::Output::finishFrame(refreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 02e3a45..1338538 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -75,10 +75,6 @@
dumpVal(out, "alpha", alpha);
dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
- out.append("\n ");
- dumpVal(out, "type", type);
- dumpVal(out, "appId", appId);
-
if (!metadata.empty()) {
out.append("\n metadata {");
for (const auto& [key, entry] : metadata) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 9e0a43a..04dceae 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -69,6 +69,19 @@
return Reversed<T>(c);
}
+struct ScaleVector {
+ float x;
+ float y;
+};
+
+// Returns a ScaleVector (x, y) such that from.scale(x, y) = to',
+// where to' will have the same size as "to". In the case where "from" and "to"
+// start at the origin to'=to.
+ScaleVector getScale(const Rect& from, const Rect& to) {
+ return {.x = static_cast<float>(to.width()) / from.width(),
+ .y = static_cast<float>(to.height()) / from.height()};
+}
+
} // namespace
std::shared_ptr<Output> createOutput(
@@ -105,32 +118,76 @@
dirtyEntireOutput();
}
-void Output::setProjection(const ui::Transform& transform, uint32_t orientation,
- const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
- const Rect& displaySpaceRect, bool needsFiltering) {
+void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+ const Rect& orientedDisplaySpaceRect) {
auto& outputState = editState();
- outputState.transform = transform;
- outputState.orientation = orientation;
- outputState.displaySpace.content = displaySpaceRect;
- // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
- outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+ outputState.displaySpace.orientation = orientation;
+ LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+ "The display bounds are unknown.");
+
+ // Compute orientedDisplaySpace
ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
- if (orientation == ui::Transform::ROT_90 || orientation == ui::Transform::ROT_270) {
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
}
outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+ outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+ // Compute displaySpace.content
+ const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
+ ui::Transform rotation;
+ if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
+ const auto displaySize = outputState.displaySpace.bounds;
+ rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
+ }
+ outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+
+ // Compute framebufferSpace
+ outputState.framebufferSpace.orientation = orientation;
+ LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+ "The framebuffer bounds are unknown.");
+ const auto scale =
+ getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
+ outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+
+ // Compute layerStackSpace
outputState.layerStackSpace.content = layerStackSpaceRect;
outputState.layerStackSpace.bounds = layerStackSpaceRect;
- outputState.needsFiltering = needsFiltering;
+ outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
+ outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
dirtyEntireOutput();
}
-void Output::setDisplaySpaceSize(const ui::Size& size) {
+void Output::setDisplaySize(const ui::Size& size) {
mRenderSurface->setDisplaySize(size);
- editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
+
+ auto& state = editState();
+
+ // Update framebuffer space
+ const Rect newBounds(size);
+ ScaleVector scale;
+ scale = getScale(state.framebufferSpace.bounds, newBounds);
+ state.framebufferSpace.bounds = newBounds;
+ state.framebufferSpace.content.scaleSelf(scale.x, scale.y);
+
+ // Update display space
+ scale = getScale(state.displaySpace.bounds, newBounds);
+ state.displaySpace.bounds = newBounds;
+ state.displaySpace.content.scaleSelf(scale.x, scale.y);
+ state.transform = state.layerStackSpace.getTransform(state.displaySpace);
+
+ // Update oriented display space
+ const auto orientation = state.displaySpace.orientation;
+ ui::Size orientedSize = size;
+ if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+ std::swap(orientedSize.width, orientedSize.height);
+ }
+ const Rect newOrientedBounds(orientedSize);
+ scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds);
+ state.orientedDisplaySpace.bounds = newOrientedBounds;
+ state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y);
dirtyEntireOutput();
}
@@ -238,8 +295,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().displaySpace.bounds = Rect(mRenderSurface->getSize());
-
+ editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
dirtyEntireOutput();
}
@@ -868,9 +924,10 @@
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
clientCompositionDisplay.clip = outputState.layerStackSpace.content;
- clientCompositionDisplay.orientation = outputState.orientation;
+ clientCompositionDisplay.orientation =
+ ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
: ui::Dataspace::UNKNOWN;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 776fdde..ee30ad8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -39,6 +39,8 @@
out.append("\n ");
dumpVal(out, "layerStackSpace", to_string(layerStackSpace));
out.append("\n ");
+ dumpVal(out, "framebufferSpace", to_string(framebufferSpace));
+ out.append("\n ");
dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace));
out.append("\n ");
dumpVal(out, "displaySpace", to_string(displaySpace));
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 376b4b3..0faab6f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -133,7 +133,8 @@
* the code below applies the primary display's inverse transform to the
* buffer
*/
- uint32_t invTransformOrient = outputState.orientation;
+ uint32_t invTransformOrient =
+ ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
// calculate the inverse transform
if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -402,13 +403,6 @@
outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type),
- static_cast<uint32_t>(outputIndependentState.appId));
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
-
for (const auto& [name, entry] : outputIndependentState.metadata) {
if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value);
error != hal::Error::NONE) {
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 2773fd3..b47f7fd 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -48,6 +48,8 @@
namespace impl {
+constexpr auto DEFAULT_USAGE = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
const compositionengine::CompositionEngine& compositionEngine,
compositionengine::Display& display,
@@ -81,7 +83,7 @@
ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
- status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+ status = native_window_set_usage(window, DEFAULT_USAGE);
ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
}
@@ -109,7 +111,7 @@
}
void RenderSurface::setProtected(bool useProtected) {
- uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+ uint64_t usageFlags = DEFAULT_USAGE;
if (useProtected) {
usageFlags |= GRALLOC_USAGE_PROTECTED;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index d889d74..d5bf569 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -30,6 +30,7 @@
namespace {
using ::testing::_;
+using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Ref;
using ::testing::Return;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 4519a9d..6d01bf1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -176,6 +176,7 @@
DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
return DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(false)
+ .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -189,6 +190,7 @@
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
StrictMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuDisplayIdGenerator;
};
struct PartialMockDisplayTestCommon : public DisplayTestCommon {
@@ -245,7 +247,7 @@
getDisplayCreationArgsForNonHWCVirtualDisplay());
EXPECT_FALSE(display->isSecure());
EXPECT_TRUE(display->isVirtual());
- EXPECT_EQ(std::nullopt, display->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
}
/*
@@ -332,6 +334,7 @@
mDisplay->setConfiguration(
DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(true)
+ .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
.setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -340,7 +343,7 @@
.setName(getDisplayNameFromCurrentTest())
.build());
- EXPECT_EQ(std::nullopt, mDisplay->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -352,6 +355,7 @@
mDisplay->setConfiguration(
DisplayCreationArgsBuilder()
.setUseHwcVirtualDisplays(false)
+ .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
.setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
.setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
.setIsSecure(false)
@@ -360,7 +364,7 @@
.setName(getDisplayNameFromCurrentTest())
.build());
- EXPECT_EQ(std::nullopt, mDisplay->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -375,16 +379,13 @@
using DisplayDisconnectTest = PartialMockDisplayTestCommon;
TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
- // The first call to disconnect will disconnect the display with the HWC and
- // set mHwcId to -1.
- EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+ // The first call to disconnect will disconnect the display with the HWC.
+ EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
mDisplay->disconnect();
- EXPECT_FALSE(mDisplay->getId());
// Subsequent calls will do nothing,
- EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+ EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(0);
mDisplay->disconnect();
- EXPECT_FALSE(mDisplay->getId());
}
/*
@@ -402,7 +403,8 @@
// Identity matrix sets an identity state value
const mat4 kIdentity;
- EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
+ EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kIdentity))
+ .Times(1);
refreshArgs.colorTransformMatrix = kIdentity;
mDisplay->setColorTransform(refreshArgs);
@@ -410,7 +412,8 @@
// Non-identity matrix sets a non-identity state value
const mat4 kNonIdentity = mat4() * 2;
- EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
+ EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kNonIdentity))
+ .Times(1);
refreshArgs.colorTransformMatrix = kNonIdentity;
mDisplay->setColorTransform(refreshArgs);
@@ -527,13 +530,14 @@
sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
StrictMock<HWC2::mock::Layer> hwcLayer;
- EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+ EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID)))
+ .WillOnce(Return(&hwcLayer));
auto outputLayer = mDisplay->createOutputLayer(layerFE);
EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
- EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+ EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer));
outputLayer.reset();
}
@@ -606,7 +610,7 @@
auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
std::shared_ptr<Display> nonHwcDisplay =
createPartialMockDisplay<Display>(mCompositionEngine, args);
- EXPECT_FALSE(nonHwcDisplay->getId());
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(nonHwcDisplay->getId()));
nonHwcDisplay->chooseCompositionStrategy();
@@ -617,7 +621,8 @@
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
+ EXPECT_CALL(mHwComposer,
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
.WillOnce(Return(INVALID_OPERATION));
mDisplay->chooseCompositionStrategy();
@@ -639,7 +644,7 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+ EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
@@ -669,7 +674,7 @@
.InSequence(s)
.WillOnce(Return(false));
- EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+ EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
.WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
@@ -699,7 +704,7 @@
TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
EXPECT_CALL(mHwComposer,
- hasDisplayCapability(DEFAULT_DISPLAY_ID,
+ hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID),
hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
.WillOnce(Return(true));
EXPECT_TRUE(mDisplay->getSkipColorTransform());
@@ -857,13 +862,16 @@
sp<Fence> layer1Fence = new Fence();
sp<Fence> layer2Fence = new Fence();
- EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
- EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
- EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+ EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
+ .WillOnce(Return(presentFence));
+ EXPECT_CALL(mHwComposer,
+ getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer1.hwc2Layer))
.WillOnce(Return(layer1Fence));
- EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
+ EXPECT_CALL(mHwComposer,
+ getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer2.hwc2Layer))
.WillOnce(Return(layer2Fence));
- EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+ EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
auto result = mDisplay->presentAndGetFrameFences();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index d21b97e..87911cc 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -66,7 +66,6 @@
MOCK_METHOD1(setTransform, Error(hal::Transform));
MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
MOCK_METHOD1(setZOrder, Error(uint32_t));
- MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
MOCK_METHOD3(setLayerGenericMetadata,
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 1dd5df4..b5cda68 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -42,63 +42,68 @@
MOCK_CONST_METHOD3(getDisplayIdentificationData,
bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
- MOCK_CONST_METHOD2(hasDisplayCapability, bool(DisplayId, hal::DisplayCapability));
+ MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
MOCK_METHOD3(allocateVirtualDisplay,
std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
- MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
- MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
+ MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
+ MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
MOCK_METHOD3(getDeviceCompositionChanges,
- status_t(DisplayId, bool,
+ status_t(HalDisplayId, bool,
std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
- status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
+ status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
ui::Dataspace));
- MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
- MOCK_METHOD2(setPowerMode, status_t(DisplayId, hal::PowerMode));
- MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
- MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
- MOCK_METHOD1(disconnectDisplay, void(DisplayId));
+ MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+ MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
+ MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
+ MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
+ MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
- MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
- MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
- MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
- MOCK_METHOD1(clearReleaseFences, void(DisplayId));
- MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*));
- MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId));
- MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode));
- MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace));
+ MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+ MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
+ MOCK_METHOD3(setOutputBuffer,
+ status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
+ MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
+ MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
+ MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
+ MOCK_CONST_METHOD2(getRenderIntents,
+ std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
+ MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
MOCK_METHOD4(getDisplayedContentSamplingAttributes,
- status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
- MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
+ status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
+ MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
MOCK_METHOD4(getDisplayedContentSample,
- status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
- MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(DisplayId, float));
- MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*));
+ status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
+ MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float));
+ MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
MOCK_METHOD2(onHotplug,
std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
- MOCK_METHOD2(setVsyncEnabled, void(DisplayId, hal::Vsync));
- MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
- MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
- MOCK_CONST_METHOD1(getConfigs,
- std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId));
- MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId));
- MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId));
- MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
- MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
+ MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
+ MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(
+ getConfigs,
+ std::vector<std::shared_ptr<const HWC2::Display::Config>>(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getActiveConfig,
+ std::shared_ptr<const HWC2::Display::Config>(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getActiveConfigIndex, int(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
+ MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
MOCK_CONST_METHOD0(isUsingVrComposer, bool());
- MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(DisplayId));
- MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
- MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+ MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
+ MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(PhysicalDisplayId));
MOCK_METHOD4(setActiveConfigWithConstraints,
- status_t(DisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
+ status_t(PhysicalDisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline*));
- MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
- MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<hal::ContentType>*));
- MOCK_METHOD2(setContentType, status_t(DisplayId, hal::ContentType));
+ MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
+ MOCK_METHOD2(getSupportedContentTypes,
+ status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
+ MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
const std::unordered_map<std::string, bool>&());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index df3da85..dcfc162 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -21,6 +21,7 @@
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
+#include <log/log.h>
#include "MockHWC2.h"
#include "MockHWComposer.h"
@@ -55,6 +56,22 @@
return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
}
+ui::Rotation toRotation(uint32_t rotationFlag) {
+ switch (rotationFlag) {
+ case ui::Transform::RotationFlags::ROT_0:
+ return ui::ROTATION_0;
+ case ui::Transform::RotationFlags::ROT_90:
+ return ui::ROTATION_90;
+ case ui::Transform::RotationFlags::ROT_180:
+ return ui::ROTATION_180;
+ case ui::Transform::RotationFlags::ROT_270:
+ return ui::ROTATION_270;
+ default:
+ LOG_FATAL("Unexpected rotation flag %d", rotationFlag);
+ return ui::Rotation(-1);
+ }
+}
+
struct OutputLayerTest : public testing::Test {
struct OutputLayer final : public impl::OutputLayer {
OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
@@ -209,7 +226,7 @@
mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.orientation = entry.display;
+ mOutputState.displaySpace.orientation = toRotation(entry.display);
EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
}
@@ -358,7 +375,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.orientation = entry.display;
+ mOutputState.displaySpace.orientation = toRotation(entry.display);
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -470,7 +487,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.orientation = entry.display;
+ mOutputState.displaySpace.orientation = toRotation(entry.display);
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -676,8 +693,6 @@
static constexpr Hwc2::IComposerClient::BlendMode kBlendMode =
static_cast<Hwc2::IComposerClient::BlendMode>(41);
static constexpr float kAlpha = 51.f;
- static constexpr uint32_t kType = 61u;
- static constexpr uint32_t kAppId = 62u;
static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
@@ -711,8 +726,6 @@
mLayerFEState.blendMode = kBlendMode;
mLayerFEState.alpha = kAlpha;
- mLayerFEState.type = kType;
- mLayerFEState.appId = kAppId;
mLayerFEState.colorTransform = kColorTransform;
mLayerFEState.color = kColor;
mLayerFEState.surfaceDamage = kSurfaceDamage;
@@ -746,7 +759,6 @@
EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
}
void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
@@ -858,7 +870,7 @@
// This test simulates a scenario where displayInstallOrientation is set to
// ROT_90. This only has an effect on the transform; orientation stays 0 (see
// DisplayDevice::setProjection).
- mOutputState.orientation = TR_IDENT;
+ mOutputState.displaySpace.orientation = ui::ROTATION_0;
mOutputState.transform = ui::Transform{TR_ROT_90};
// Buffers are pre-rotated based on the transform hint (ROT_90); their
// geomBufferTransform is set to the inverse transform.
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 3dd26c0..1e10365 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -235,40 +235,121 @@
* Output::setProjection()
*/
-TEST_F(OutputTest, setProjectionTriviallyWorks) {
- const ui::Transform transform{ui::Transform::ROT_180};
- const int32_t orientation = 123;
- const Rect frame{1, 2, 3, 4};
- const Rect viewport{5, 6, 7, 8};
- const Rect destinationClip{13, 14, 15, 16};
- const bool needsFiltering = true;
+TEST_F(OutputTest, setProjectionWorks) {
+ const Rect displayRect{0, 0, 1000, 2000};
+ mOutput->editState().displaySpace.bounds = displayRect;
+ mOutput->editState().framebufferSpace.bounds = displayRect;
- mOutput->setProjection(transform, orientation, frame, viewport, destinationClip,
- needsFiltering);
+ const ui::Rotation orientation = ui::ROTATION_90;
+ const Rect frame{50, 60, 100, 100};
+ const Rect viewport{10, 20, 30, 40};
- EXPECT_THAT(mOutput->getState().transform, transform);
- EXPECT_EQ(orientation, mOutput->getState().orientation);
+ mOutput->setProjection(orientation, viewport, frame);
+
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
- EXPECT_EQ(destinationClip, mOutput->getState().displaySpace.content);
- EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
+
+ const auto state = mOutput->getState();
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(viewport, state.layerStackSpace.content);
+ EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+ EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
+ EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+}
+
+TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
+ const Rect displayRect{0, 0, 1000, 2000};
+ const Rect framebufferRect{0, 0, 500, 1000};
+ mOutput->editState().displaySpace.bounds = displayRect;
+ mOutput->editState().framebufferSpace.bounds = framebufferRect;
+
+ const ui::Rotation orientation = ui::ROTATION_90;
+ const Rect frame{50, 60, 100, 100};
+ const Rect viewport{10, 20, 30, 40};
+
+ mOutput->setProjection(orientation, viewport, frame);
+
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+
+ const auto state = mOutput->getState();
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(viewport, state.layerStackSpace.content);
+ EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(frame, state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
+ EXPECT_EQ(orientation, state.displaySpace.orientation);
+
+ EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
+ EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
}
/*
- * Output::setDisplaySpaceSize()
+ * Output::setDisplaySize()
*/
-TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
- const ui::Size displaySize{200, 400};
+TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
+ mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
+ mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
+ mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
+ mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
+ mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
+ mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
+ mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
- EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
- EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+ const ui::Size newDisplaySize{500, 1000};
- mOutput->setDisplaySpaceSize(displaySize);
+ EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1);
- EXPECT_EQ(Rect(displaySize), mOutput->getState().displaySpace.bounds);
+ mOutput->setDisplaySize(newDisplaySize);
- EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+ const auto state = mOutput->getState();
+
+ const Rect displayRect(newDisplaySize);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
+ EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+
+ EXPECT_EQ(displayRect, state.displaySpace.bounds);
+ EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+
+ EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
+ EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content);
+ EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+
+ EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+ EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
}
/*
@@ -429,7 +510,7 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().displaySpace.bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
}
/*
@@ -2785,9 +2866,10 @@
mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
+ mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
- mOutput.mState.orientation = kDefaultOutputOrientation;
+ mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+ mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.dataspace = kDefaultOutputDataspace;
mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
mOutput.mState.isSecure = false;
@@ -2822,7 +2904,9 @@
// Call this member function to start using the mini-DSL defined above.
[[nodiscard]] auto verify() { return ExecuteState::make(this); }
- static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+ static constexpr ui::Rotation kDefaultOutputOrientation = ui::ROTATION_0;
+ static constexpr uint32_t kDefaultOutputOrientationFlags =
+ ui::Transform::toRotationFlags(kDefaultOutputOrientation);
static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
static constexpr float kDefaultMaxLuminance = 0.9f;
@@ -3119,7 +3203,7 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ Region::INVALID_REGION, kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3130,7 +3214,7 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ Region::INVALID_REGION, kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3142,7 +3226,7 @@
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
- kDefaultOutputOrientation})
+ kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3154,7 +3238,7 @@
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace,
kDefaultColorTransformMat, Region::INVALID_REGION,
- kDefaultOutputOrientation})
+ kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3166,7 +3250,7 @@
.andIfSkipColorTransform(true)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientation})
+ Region::INVALID_REGION, kDefaultOutputOrientationFlags})
.execute()
.expectAFenceWasReturned();
}
@@ -3412,8 +3496,9 @@
mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
mOutput.mState.layerStackSpace.content = kDisplayViewport;
mOutput.mState.displaySpace.content = kDisplayDestinationClip;
- mOutput.mState.transform = ui::Transform{kDisplayOrientation};
- mOutput.mState.orientation = kDisplayOrientation;
+ mOutput.mState.transform =
+ ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
+ mOutput.mState.displaySpace.orientation = kDisplayOrientation;
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = false;
@@ -3437,7 +3522,7 @@
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
}
- static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+ static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
static const Rect kDisplayFrame;
@@ -3921,14 +4006,14 @@
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
- const uint32_t kPortraitOrientation = TR_ROT_90;
+ const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
mOutput.mState.layerStackSpace.content = kPortraitViewport;
mOutput.mState.displaySpace.content = kPortraitDestinationClip;
- mOutput.mState.transform = ui::Transform{kPortraitOrientation};
- mOutput.mState.orientation = kPortraitOrientation;
+ mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
+ mOutput.mState.displaySpace.orientation = kPortraitOrientation;
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = true;
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
new file mode 100644
index 0000000..704f5a8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 <compositionengine/ProjectionSpace.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+// Returns a rectangular strip along the side of the given rect pointed by
+// rotation. E.g. if rotation is ROTATION_0, the srip will be along the top
+// side, if it is ROTATION_90 the stip will be along the right wall.
+// One of the dimensions of the strip will be 0 and the other one will match
+// the length of the corresponding side.
+// The strip will be contained inside the given rect.
+Rect getSideStrip(const Rect& rect, ui::Rotation rotation) {
+ int width, height;
+ if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+ width = 0;
+ height = rect.height();
+ } else {
+ width = rect.width();
+ height = 0;
+ }
+
+ if (rotation == ui::ROTATION_0 || rotation == ui::ROTATION_270) {
+ return Rect(rect.left, rect.top, rect.left + width, rect.top + height);
+ }
+
+ if (rotation == ui::ROTATION_90) {
+ return Rect(rect.right, rect.top, rect.right + width, rect.top + height);
+ }
+
+ if (rotation == ui::ROTATION_180) {
+ return Rect(rect.left, rect.bottom, rect.left + width, rect.bottom + height);
+ }
+
+ return Rect::INVALID_RECT;
+}
+} // namespace
+
+TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
+ ProjectionSpace space;
+ space.content = Rect(100, 200);
+ space.bounds = Rect(100, 200);
+
+ const ui::Transform identity;
+ for (int rotation = 0; rotation <= 3; rotation++) {
+ space.orientation = ui::Rotation(rotation);
+ EXPECT_EQ(space.getTransform(space), identity);
+ }
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
+ ProjectionSpace source;
+ source.content = Rect(10, 10, 20, 20);
+ source.bounds = Rect(100, 200);
+
+ ProjectionSpace dest;
+ dest.content = Rect(10, 20, 30, 20);
+ dest.bounds = source.bounds;
+
+ const auto transform = source.getTransform(dest);
+ EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
+ ProjectionSpace source;
+ source.content = Rect(0, 0, 20, 20);
+ source.bounds = Rect(100, 200);
+
+ ProjectionSpace dest;
+ dest.content = Rect(0, 0, 40, 30);
+ dest.bounds = source.bounds;
+
+ const auto transform = source.getTransform(dest);
+ EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getSideStripTest) {
+ const Rect rect(10, 20, 40, 100);
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_0), Rect(10, 20, 40, 20));
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_90), Rect(40, 20, 40, 100));
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_180), Rect(10, 100, 40, 100));
+ EXPECT_EQ(getSideStrip(rect, ui::ROTATION_270), Rect(10, 20, 10, 100));
+}
+
+void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
+ const auto transform = source.getTransform(dest);
+ EXPECT_EQ(transform.transform(source.content), dest.content)
+ << "Source content doesn't map to dest content when projecting " << to_string(source)
+ << " onto " << to_string(dest);
+
+ // We take a strip at the top (according to the orientation) of each
+ // content rect and verify that transform maps between them. This way we
+ // verify that the transform is rotating properly.
+ // In the following example the strip is marked with asterisks:
+ //
+ // ******* +-------*
+ // | | | *
+ // | | | *
+ // +-----+ +-------*
+ // source(ROTATION_0) dest (ROTATION_90)
+ const auto sourceStrip = getSideStrip(source.content, source.orientation);
+ const auto destStrip = getSideStrip(dest.content, dest.orientation);
+ ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
+ ASSERT_NE(destStrip, Rect::INVALID_RECT);
+ const auto mappedStrip = transform.transform(sourceStrip);
+ EXPECT_EQ(mappedStrip, destStrip)
+ << to_string(sourceStrip) << " maps to " << to_string(mappedStrip) << " instead of "
+ << to_string(destStrip) << " when projecting " << to_string(source) << " onto "
+ << to_string(dest);
+}
+
+TEST(ProjectionSpaceTest, getTransformWithOrienations) {
+ ProjectionSpace source;
+ source.bounds = Rect(12, 13, 678, 789);
+ source.content = Rect(40, 50, 234, 343);
+ ProjectionSpace dest;
+ dest.bounds = Rect(17, 18, 879, 564);
+ dest.content = Rect(43, 52, 432, 213);
+
+ for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
+ source.orientation = ui::Rotation(sourceRot);
+ for (int destRot = 0; destRot <= 3; destRot++) {
+ dest.orientation = ui::Rotation(destRot);
+ testTransform(source, dest);
+ }
+ }
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 519cc57..6ce8a6b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -33,7 +33,7 @@
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = {PhysicalDisplayId(123u)};
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
using testing::_;
@@ -48,7 +48,7 @@
class RenderSurfaceTest : public testing::Test {
public:
RenderSurfaceTest() {
- EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+ EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(DEFAULT_DISPLAY_ID));
EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
@@ -82,7 +82,8 @@
EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+ .WillOnce(Return(NO_ERROR));
mSurface.initialize();
}
@@ -136,7 +137,9 @@
TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ EXPECT_CALL(*mNativeWindow,
+ setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_PROTECTED))
.WillOnce(Return(NO_ERROR));
mSurface.setProtected(true);
@@ -145,7 +148,8 @@
TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+ .WillOnce(Return(NO_ERROR));
mSurface.setProtected(false);
EXPECT_FALSE(mSurface.isProtected());
@@ -153,9 +157,12 @@
TEST_F(RenderSurfaceTest, setProtectedEnableAndDisable) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ EXPECT_CALL(*mNativeWindow,
+ setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_PROTECTED))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE))
+ .WillOnce(Return(NO_ERROR));
mSurface.setProtected(true);
EXPECT_TRUE(mSurface.isProtected());
@@ -165,7 +172,9 @@
TEST_F(RenderSurfaceTest, setProtectedEnableWithError) {
EXPECT_FALSE(mSurface.isProtected());
- EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+ EXPECT_CALL(*mNativeWindow,
+ setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_PROTECTED))
.WillOnce(Return(INVALID_OPERATION));
mSurface.setProtected(true);
EXPECT_FALSE(mSurface.isProtected());
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index ea59692..5bd7a1f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -156,84 +156,38 @@
}
void DisplayDevice::setDisplaySize(int width, int height) {
- mCompositionDisplay->setDisplaySpaceSize(ui::Size(width, height));
+ LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
+ mCompositionDisplay->setDisplaySize(ui::Size(width, height));
}
void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
Rect orientedDisplaySpaceRect) {
mOrientation = orientation;
- const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
- const int displayWidth = displayBounds.width();
- const int displayHeight = displayBounds.height();
+ if (isPrimary()) {
+ sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
+ }
if (!orientedDisplaySpaceRect.isValid()) {
- // the destination frame can be invalid if it has never been set,
- // in that case we assume the whole display frame.
- orientedDisplaySpaceRect = Rect(displayWidth, displayHeight);
+ // The destination frame can be invalid if it has never been set,
+ // in that case we assume the whole display size.
+ orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
}
if (layerStackSpaceRect.isEmpty()) {
- // layerStackSpaceRect can be invalid if it has never been set, in that case
- // we assume the whole display size.
- // It's also invalid to have an empty layerStackSpaceRect, so we handle that
- // case in the same way.
- layerStackSpaceRect = Rect(displayWidth, displayHeight);
+ // The layerStackSpaceRect can be invalid if it has never been set, in that case
+ // we assume the whole framebuffer size.
+ layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
}
}
- ui::Transform logicalTranslation, physicalTranslation, scale;
- const float sourceWidth = layerStackSpaceRect.width();
- const float sourceHeight = layerStackSpaceRect.height();
- const float destWidth = orientedDisplaySpaceRect.width();
- const float destHeight = orientedDisplaySpaceRect.height();
- if (sourceWidth != destWidth || sourceHeight != destHeight) {
- const float scaleX = destWidth / sourceWidth;
- const float scaleY = destHeight / sourceHeight;
- scale.set(scaleX, 0, 0, scaleY);
- }
-
- const float sourceX = layerStackSpaceRect.left;
- const float sourceY = layerStackSpaceRect.top;
- const float destX = orientedDisplaySpaceRect.left;
- const float destY = orientedDisplaySpaceRect.top;
- logicalTranslation.set(-sourceX, -sourceY);
- physicalTranslation.set(destX, destY);
-
// We need to take care of display rotation for globalTransform for case if the panel is not
// installed aligned with device orientation.
const auto transformOrientation = orientation + mPhysicalOrientation;
- const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(transformOrientation);
- ui::Transform rotation;
- if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
- rotation.set(transformOrientationFlags, displayWidth, displayHeight);
- }
-
- // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
- // Apply the logical translation, scale to physical size, apply the
- // physical translation and finally rotate to the physical orientation.
- ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
-
- const uint8_t type = globalTransform.getType();
- const bool needsFiltering =
- (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
-
- Rect displaySpaceRect = globalTransform.transform(layerStackSpaceRect);
- if (displaySpaceRect.isEmpty()) {
- displaySpaceRect = displayBounds;
- }
- // Make sure the displaySpaceRect is contained in the display bounds
- displaySpaceRect.intersect(displayBounds, &displaySpaceRect);
-
- if (isPrimary()) {
- sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
- }
-
- getCompositionDisplay()->setProjection(globalTransform, transformOrientationFlags,
- orientedDisplaySpaceRect, layerStackSpaceRect,
- displaySpaceRect, needsFiltering);
+ getCompositionDisplay()->setProjection(transformOrientation, layerStackSpaceRect,
+ orientedDisplaySpaceRect);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
@@ -241,17 +195,12 @@
}
std::string DisplayDevice::getDebugName() const {
- std::string displayId;
- if (const auto id = getId()) {
- displayId = to_string(*id) + ", ";
- }
-
const char* type = "virtual";
if (mConnectionType) {
type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external";
}
- return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type,
+ return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type,
isPrimary() ? ", primary" : "", mDisplayName.c_str());
}
@@ -275,9 +224,7 @@
return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
}
-// ----------------------------------------------------------------------------
-
-const std::optional<DisplayId>& DisplayDevice::getId() const {
+DisplayId DisplayDevice::getId() const {
return mCompositionDisplay->getId();
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 35a8b62..fa684c0 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -27,6 +27,7 @@
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
#include <system/window.h>
+#include <ui/DisplayId.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayState.h>
#include <ui/GraphicTypes.h>
@@ -104,7 +105,15 @@
bool needsFiltering() const;
ui::LayerStack getLayerStack() const;
- const std::optional<DisplayId>& getId() const;
+ // Returns the physical ID of this display. This function asserts the ID is physical and it
+ // shouldn't be called for other display types, e.g. virtual.
+ PhysicalDisplayId getPhysicalId() const {
+ const auto displayIdOpt = PhysicalDisplayId::tryCast(getId());
+ LOG_FATAL_IF(!displayIdOpt);
+ return *displayIdOpt;
+ }
+
+ DisplayId getId() const;
const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
int32_t getSequenceId() const { return mSequenceId; }
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index a3f1b52..1bf43da 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -117,63 +117,7 @@
namespace impl {
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize)
- : CommandWriterBase(initialMaxSize) {}
-
-Composer::CommandWriter::~CommandWriter()
-{
-}
-
-void Composer::CommandWriter::setLayerInfo(uint32_t type, uint32_t appId)
-{
- constexpr uint16_t kSetLayerInfoLength = 2;
- beginCommand(static_cast<V2_1::IComposerClient::Command>(
- IVrComposerClient::VrCommand::SET_LAYER_INFO),
- kSetLayerInfoLength);
- write(type);
- write(appId);
- endCommand();
-}
-
-void Composer::CommandWriter::setClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata)
-{
- constexpr uint16_t kSetClientTargetMetadataLength = 7;
- beginCommand(static_cast<V2_1::IComposerClient::Command>(
- IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA),
- kSetClientTargetMetadataLength);
- writeBufferMetadata(metadata);
- endCommand();
-}
-
-void Composer::CommandWriter::setLayerBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata)
-{
- constexpr uint16_t kSetLayerBufferMetadataLength = 7;
- beginCommand(static_cast<V2_1::IComposerClient::Command>(
- IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA),
- kSetLayerBufferMetadataLength);
- writeBufferMetadata(metadata);
- endCommand();
-}
-
-void Composer::CommandWriter::writeBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata)
-{
- write(metadata.width);
- write(metadata.height);
- write(metadata.stride);
- write(metadata.layerCount);
- writeSigned(static_cast<int32_t>(metadata.format));
- write64(metadata.usage);
-}
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
-Composer::Composer(const std::string& serviceName)
- : mWriter(kWriterInitialSize),
- mIsUsingVrComposer(serviceName == std::string("vr"))
-{
+Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
mComposer = V2_1::IComposer::getService(serviceName);
if (mComposer == nullptr) {
@@ -215,15 +159,6 @@
if (mClient == nullptr) {
LOG_ALWAYS_FATAL("failed to create composer client");
}
-
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- if (mIsUsingVrComposer) {
- sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient);
- if (vrClient == nullptr) {
- LOG_ALWAYS_FATAL("failed to create vr composer client");
- }
- }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
}
Composer::~Composer() = default;
@@ -262,10 +197,6 @@
}
}
-bool Composer::isRemote() {
- return mClient->isRemote();
-}
-
void Composer::resetCommands() {
mWriter.reset();
}
@@ -587,20 +518,6 @@
{
mWriter.selectDisplay(display);
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- if (mIsUsingVrComposer && target.get()) {
- IVrComposerClient::BufferMetadata metadata = {
- .width = target->getWidth(),
- .height = target->getHeight(),
- .stride = target->getStride(),
- .layerCount = target->getLayerCount(),
- .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()),
- .usage = target->getUsage(),
- };
- mWriter.setClientTargetMetadata(metadata);
- }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
const native_handle_t* handle = nullptr;
if (target.get()) {
handle = target->getNativeBuffer()->handle;
@@ -720,20 +637,6 @@
mWriter.selectDisplay(display);
mWriter.selectLayer(layer);
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- if (mIsUsingVrComposer && buffer.get()) {
- IVrComposerClient::BufferMetadata metadata = {
- .width = buffer->getWidth(),
- .height = buffer->getHeight(),
- .stride = buffer->getStride(),
- .layerCount = buffer->getLayerCount(),
- .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()),
- .usage = buffer->getUsage(),
- };
- mWriter.setLayerBufferMetadata(metadata);
- }
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
const native_handle_t* handle = nullptr;
if (buffer.get()) {
handle = buffer->getNativeBuffer()->handle;
@@ -850,27 +753,6 @@
return Error::NONE;
}
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type,
- uint32_t appId)
-{
- if (mIsUsingVrComposer) {
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerInfo(type, appId);
- }
- return Error::NONE;
-}
-#else
-Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) {
- if (mIsUsingVrComposer) {
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- }
- return Error::NONE;
-}
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
Error Composer::execute()
{
// prepare input command queue
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 00ef782..5b66809 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -27,9 +27,6 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
@@ -47,10 +44,6 @@
namespace Hwc2 {
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-using frameworks::vr::composer::V2_0::IVrComposerClient;
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
-
namespace types = hardware::graphics::common;
namespace V2_1 = hardware::graphics::composer::V2_1;
@@ -91,11 +84,6 @@
virtual void registerCallback(const sp<IComposerCallback>& callback) = 0;
- // Returns true if the connected composer service is running in a remote
- // process, false otherwise. This will return false if the service is
- // configured in passthrough mode, for example.
- virtual bool isRemote() = 0;
-
// 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;
@@ -104,7 +92,6 @@
virtual Error executeCommands() = 0;
virtual uint32_t getMaxVirtualDisplayCount() = 0;
- virtual bool isUsingVrComposer() const = 0;
virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
Display* outDisplay) = 0;
virtual Error destroyVirtualDisplay(Display display) = 0;
@@ -188,7 +175,6 @@
virtual Error setLayerVisibleRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& visible) = 0;
virtual Error setLayerZOrder(Display display, Layer layer, uint32_t z) = 0;
- virtual Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) = 0;
// Composer HAL 2.2
virtual Error setLayerPerFrameMetadata(
@@ -344,11 +330,6 @@
void registerCallback(const sp<IComposerCallback>& callback) override;
- // Returns true if the connected composer service is running in a remote
- // process, false otherwise. This will return false if the service is
- // configured in passthrough mode, for example.
- bool isRemote() override;
-
// 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;
@@ -357,7 +338,6 @@
Error executeCommands() override;
uint32_t getMaxVirtualDisplayCount() override;
- bool isUsingVrComposer() const override { return mIsUsingVrComposer; }
Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
Display* outDisplay) override;
Error destroyVirtualDisplay(Display display) override;
@@ -436,7 +416,6 @@
Error setLayerVisibleRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& visible) override;
Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
- Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) override;
// Composer HAL 2.2
Error setLayerPerFrameMetadata(
@@ -490,29 +469,11 @@
IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
private:
-#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
- class CommandWriter : public CommandWriterBase {
- public:
- explicit CommandWriter(uint32_t initialMaxSize);
- ~CommandWriter() override;
-
- void setLayerInfo(uint32_t type, uint32_t appId);
- void setClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
- void setLayerBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
-
- private:
- void writeBufferMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
- };
-#else
class CommandWriter : public CommandWriterBase {
public:
explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
~CommandWriter() override {}
};
-#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
// Many public functions above simply write a command into the command
// queue to batch the calls. validateDisplay and presentDisplay will call
@@ -531,10 +492,6 @@
64 * 1024 / sizeof(uint32_t) - 16;
CommandWriter mWriter;
CommandReader mReader;
-
- // When true, the we attach to the vr_hwcomposer service instead of the
- // hwcomposer. This allows us to redirect surfaces to 3d surfaces in vr.
- const bool mIsUsingVrComposer;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 4c3b3e5..14b54cd 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -56,7 +56,7 @@
*
*/
-FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer,
uint32_t maxWidth, uint32_t maxHeight)
: ConsumerBase(consumer),
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index a1859f3..759943a 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
#include <compositionengine/DisplaySurface.h>
#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
+#include <ui/DisplayId.h>
#include <ui/Size.h>
#include "DisplayIdentification.h"
@@ -39,7 +40,7 @@
class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
public:
- FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+ FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
uint32_t maxHeight);
@@ -69,7 +70,7 @@
status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
sp<Fence>& outFence, ui::Dataspace& outDataspace);
- const DisplayId mDisplayId;
+ const PhysicalDisplayId mDisplayId;
// Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
// the device.
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 08559bd..e6bff04 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -977,12 +977,6 @@
return static_cast<Error>(intError);
}
-Error Layer::setInfo(uint32_t type, uint32_t appId)
-{
- auto intError = mComposer.setLayerInfo(mDisplayId, mId, type, appId);
- return static_cast<Error>(intError);
-}
-
// Composer HAL 2.3
Error Layer::setColorTransform(const android::mat4& matrix) {
if (matrix == mColorMatrix) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 12f26f6..89df84b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -240,15 +240,14 @@
class Display : public HWC2::Display {
public:
- Display(android::Hwc2::Composer& composer,
- const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id,
- hal::DisplayType type);
+ Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId,
+ hal::DisplayType);
~Display() override;
// Required by HWC2
hal::Error acceptChanges() override;
hal::Error createLayer(Layer** outLayer) override;
- hal::Error destroyLayer(Layer* layer) override;
+ hal::Error destroyLayer(Layer*) override;
hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
hal::Error getActiveConfigIndex(int* outIndex) const override;
hal::Error getChangedCompositionTypes(
@@ -258,8 +257,7 @@
int32_t getSupportedPerFrameMetadata() const override;
hal::Error getRenderIntents(hal::ColorMode colorMode,
std::vector<hal::RenderIntent>* outRenderIntents) const override;
- hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace,
- android::mat4* outMatrix) override;
+ hal::Error getDataspaceSaturationMatrix(hal::Dataspace, android::mat4* outMatrix) override;
// Doesn't call into the HWC2 device, so no errors are possible
std::vector<std::shared_ptr<const Config>> getConfigs() const override;
@@ -285,11 +283,11 @@
hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
const android::sp<android::Fence>& acquireFence,
hal::Dataspace dataspace) override;
- hal::Error setColorMode(hal::ColorMode mode, hal::RenderIntent renderIntent) override;
+ hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
- hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+ hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
const android::sp<android::Fence>& releaseFence) override;
- hal::Error setPowerMode(hal::PowerMode mode) override;
+ hal::Error setPowerMode(hal::PowerMode) override;
hal::Error setVsyncEnabled(hal::Vsync enabled) override;
hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
@@ -317,13 +315,13 @@
virtual bool isVsyncPeriodSwitchSupported() const override;
private:
- int32_t getAttribute(hal::HWConfigId configId, hal::Attribute attribute);
- void loadConfig(hal::HWConfigId configId);
+ int32_t getAttribute(hal::HWConfigId, hal::Attribute);
+ void loadConfig(hal::HWConfigId);
void loadConfigs();
// This may fail (and return a null pointer) if no layer with this ID exists
// on this display
- Layer* getLayerById(hal::HWLayerId id) const;
+ Layer* getLayerById(hal::HWLayerId) const;
friend android::TestableSurfaceFlinger;
@@ -378,7 +376,6 @@
[[clang::warn_unused_result]] virtual hal::Error setVisibleRegion(
const android::Region& region) = 0;
[[clang::warn_unused_result]] virtual hal::Error setZOrder(uint32_t z) = 0;
- [[clang::warn_unused_result]] virtual hal::Error setInfo(uint32_t type, uint32_t appId) = 0;
// Composer HAL 2.3
[[clang::warn_unused_result]] virtual hal::Error setColorTransform(
@@ -420,7 +417,6 @@
hal::Error setTransform(hal::Transform transform) override;
hal::Error setVisibleRegion(const android::Region& region) override;
hal::Error setZOrder(uint32_t z) override;
- hal::Error setInfo(uint32_t type, uint32_t appId) override;
// Composer HAL 2.3
hal::Error setColorTransform(const android::mat4& matrix) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 05ef599..2fdbd3a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -186,7 +186,7 @@
return mCapabilities.count(capability) > 0;
}
-bool HWComposer::hasDisplayCapability(DisplayId displayId,
+bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
hal::DisplayCapability capability) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
@@ -214,10 +214,8 @@
RETURN_IF_INVALID_DISPLAY(*displayId, false);
auto& displayData = mDisplayData[*displayId];
- if (displayData.isVirtual) {
- LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
- return false;
- }
+ LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+ __FUNCTION__, to_string(*displayId).c_str());
{
std::lock_guard lock(displayData.lastHwVsyncLock);
@@ -244,11 +242,6 @@
std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
ui::PixelFormat* format) {
- if (mRemainingHwcVirtualDisplays == 0) {
- ALOGE("%s: No remaining virtual displays", __FUNCTION__);
- return {};
- }
-
if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
(width > SurfaceFlinger::maxVirtualDisplaySize ||
height > SurfaceFlinger::maxVirtualDisplaySize)) {
@@ -256,31 +249,28 @@
height, SurfaceFlinger::maxVirtualDisplaySize);
return {};
}
+
+ const auto displayId = mVirtualIdGenerator.nextId();
+ if (!displayId) {
+ ALOGE("%s: No remaining virtual displays", __FUNCTION__);
+ return {};
+ }
+
hal::HWDisplayId hwcDisplayId = 0;
const auto error = static_cast<hal::Error>(
mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
if (error != hal::Error::NONE) {
ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
+ mVirtualIdGenerator.markUnused(*displayId);
return {};
}
auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
hwcDisplayId, hal::DisplayType::VIRTUAL);
display->setConnected(true);
-
- DisplayId displayId;
- if (mFreeVirtualDisplayIds.empty()) {
- displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
- } else {
- displayId = *mFreeVirtualDisplayIds.begin();
- mFreeVirtualDisplayIds.erase(displayId);
- }
-
- auto& displayData = mDisplayData[displayId];
+ auto& displayData = mDisplayData[*displayId];
displayData.hwcDisplay = std::move(display);
displayData.isVirtual = true;
-
- --mRemainingHwcVirtualDisplays;
return displayId;
}
@@ -301,7 +291,7 @@
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
}
-HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
+HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
HWC2::Layer* layer;
@@ -310,14 +300,14 @@
return layer;
}
-void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
+void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
RETURN_IF_HWC_ERROR(error, displayId);
}
-nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
+nsecs_t HWComposer::getRefreshTimestamp(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
const auto& displayData = mDisplayData.at(displayId);
// this returns the last refresh timestamp.
@@ -329,13 +319,13 @@
return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
}
-bool HWComposer::isConnected(DisplayId displayId) const {
+bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
return mDisplayData.at(displayId).hwcDisplay->isConnected();
}
std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
- DisplayId displayId) const {
+ PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
const auto& displayData = mDisplayData.at(displayId);
@@ -349,7 +339,7 @@
}
std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
- DisplayId displayId) const {
+ PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
std::shared_ptr<const HWC2::Display::Config> config;
@@ -371,7 +361,7 @@
// Composer 2.4
-DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const {
+DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal);
const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
@@ -386,12 +376,12 @@
return type;
}
-bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
}
-nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+nsecs_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
nsecs_t vsyncPeriodNanos;
@@ -400,7 +390,7 @@
return vsyncPeriodNanos;
}
-int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
+int HWComposer::getActiveConfigIndex(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, -1);
int index;
@@ -420,7 +410,7 @@
return index;
}
-std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const {
+std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
std::vector<ui::ColorMode> modes;
@@ -429,7 +419,7 @@
return modes;
}
-status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+status_t HWComposer::setActiveColorMode(PhysicalDisplayId displayId, ui::ColorMode mode,
ui::RenderIntent renderIntent) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -443,14 +433,12 @@
return NO_ERROR;
}
-void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
+void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
- if (displayData.isVirtual) {
- LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
- return;
- }
+ LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+ __FUNCTION__, to_string(displayId).c_str());
// NOTE: we use our own internal lock here because we have to call
// into the HWC with the lock held, and we want to make sure
@@ -471,7 +459,7 @@
ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0);
}
-status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
+status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
ui::Dataspace dataspace) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -484,7 +472,7 @@
}
status_t HWComposer::getDeviceCompositionChanges(
- DisplayId displayId, bool frameUsesClientComposition,
+ HalDisplayId displayId, bool frameUsesClientComposition,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -554,12 +542,12 @@
return NO_ERROR;
}
-sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
+sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
return mDisplayData.at(displayId).lastPresentFence;
}
-sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
+sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
const auto& displayFences = mDisplayData.at(displayId).releaseFences;
auto fence = displayFences.find(layer);
@@ -570,7 +558,7 @@
return fence->second;
}
-status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
ATRACE_CALL();
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -598,14 +586,12 @@
return NO_ERROR;
}
-status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) {
+status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto& displayData = mDisplayData[displayId];
- if (displayData.isVirtual) {
- LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
- return INVALID_OPERATION;
- }
+ LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+ __FUNCTION__, to_string(displayId).c_str());
if (mode == hal::PowerMode::OFF) {
setVsyncEnabled(displayId, hal::Vsync::DISABLE);
@@ -654,7 +640,8 @@
}
status_t HWComposer::setActiveConfigWithConstraints(
- DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints,
+ PhysicalDisplayId displayId, size_t configId,
+ const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -671,7 +658,7 @@
return NO_ERROR;
}
-status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) {
+status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& transform) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -684,15 +671,14 @@
return NO_ERROR;
}
-void HWComposer::disconnectDisplay(DisplayId displayId) {
+void HWComposer::disconnectDisplay(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
// If this was a virtual display, add its slot back for reuse by future
// virtual displays
if (displayData.isVirtual) {
- mFreeVirtualDisplayIds.insert(displayId);
- ++mRemainingHwcVirtualDisplays;
+ mVirtualIdGenerator.markUnused(*HalVirtualDisplayId::tryCast(displayId));
}
const auto hwcDisplayId = displayData.hwcDisplay->getId();
@@ -707,27 +693,25 @@
mDisplayData.erase(displayId);
}
-status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto& displayData = mDisplayData[displayId];
- if (!displayData.isVirtual) {
- LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display");
- return INVALID_OPERATION;
- }
+ LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
+ __FUNCTION__, to_string(displayId).c_str());
auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
-void HWComposer::clearReleaseFences(DisplayId displayId) {
+void HWComposer::clearReleaseFences(HalDisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId);
mDisplayData[displayId].releaseFences.clear();
}
-status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) {
+status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* outCapabilities) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
@@ -736,12 +720,12 @@
return NO_ERROR;
}
-int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const {
+int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
}
-std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId,
+std::vector<ui::RenderIntent> HWComposer::getRenderIntents(HalDisplayId displayId,
ui::ColorMode colorMode) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
@@ -751,7 +735,7 @@
return renderIntents;
}
-mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) {
+mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Dataspace dataspace) {
RETURN_IF_INVALID_DISPLAY(displayId, {});
mat4 matrix;
@@ -761,7 +745,7 @@
return matrix;
}
-status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+status_t HWComposer::getDisplayedContentSamplingAttributes(HalDisplayId displayId,
ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) {
@@ -775,7 +759,7 @@
return NO_ERROR;
}
-status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+status_t HWComposer::setDisplayContentSamplingEnabled(HalDisplayId displayId, bool enabled,
uint8_t componentMask, uint64_t maxFrames) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error =
@@ -789,7 +773,7 @@
return NO_ERROR;
}
-status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+status_t HWComposer::getDisplayedContentSample(HalDisplayId displayId, uint64_t maxFrames,
uint64_t timestamp, DisplayedFrameStats* outStats) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error =
@@ -799,7 +783,8 @@
return NO_ERROR;
}
-std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
+std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
+ float brightness) {
RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX));
auto& display = mDisplayData[displayId].hwcDisplay;
@@ -816,11 +801,7 @@
});
}
-bool HWComposer::isUsingVrComposer() const {
- return getComposer()->isUsingVrComposer();
-}
-
-status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
if (error == hal::Error::UNSUPPORTED) {
@@ -834,7 +815,7 @@
}
status_t HWComposer::getSupportedContentTypes(
- DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
+ PhysicalDisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error =
mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
@@ -844,7 +825,7 @@
return NO_ERROR;
}
-status_t HWComposer::setContentType(DisplayId displayId, hal::ContentType contentType) {
+status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentType contentType) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
if (error == hal::Error::UNSUPPORTED) {
@@ -886,11 +867,6 @@
bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
bool hasDisplayIdentificationData) const {
- if (isUsingVrComposer() && mInternalHwcDisplayId) {
- ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
- return true;
- }
-
if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
hwcDisplayId);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 698e3af..028a9a4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -38,6 +38,7 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
+#include "DisplayIdGenerator.h"
#include "DisplayIdentification.h"
#include "HWC2.h"
#include "Hal.h"
@@ -88,7 +89,7 @@
DisplayIdentificationData* outData) const = 0;
virtual bool hasCapability(hal::Capability) const = 0;
- virtual bool hasDisplayCapability(DisplayId, hal::DisplayCapability) const = 0;
+ virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -97,9 +98,9 @@
virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
// Attempts to create a new layer on this display
- virtual HWC2::Layer* createLayer(DisplayId) = 0;
+ virtual HWC2::Layer* createLayer(HalDisplayId) = 0;
// Destroy a previously created layer
- virtual void destroyLayer(DisplayId, HWC2::Layer*) = 0;
+ virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0;
// Gets any required composition change requests from the HWC device.
//
@@ -109,61 +110,60 @@
// with fewer handshakes, but this does not work if client composition is
// expected.
virtual status_t getDeviceCompositionChanges(
- DisplayId, bool frameUsesClientComposition,
+ HalDisplayId, bool frameUsesClientComposition,
std::optional<DeviceRequestedChanges>* outChanges) = 0;
- virtual status_t setClientTarget(DisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+ virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
// Present layers to the display and read releaseFences.
- virtual status_t presentAndGetReleaseFences(DisplayId) = 0;
+ virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
// set power mode
- virtual status_t setPowerMode(DisplayId, hal::PowerMode) = 0;
+ virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
// Sets a color transform to be applied to the result of composition
- virtual status_t setColorTransform(DisplayId, const mat4& transform) = 0;
+ virtual status_t setColorTransform(HalDisplayId, const mat4& transform) = 0;
- // reset state when an external, non-virtual display is disconnected
- virtual void disconnectDisplay(DisplayId) = 0;
+ // reset state when a display is disconnected
+ virtual void disconnectDisplay(HalDisplayId) = 0;
// get the present fence received from the last call to present.
- virtual sp<Fence> getPresentFence(DisplayId) const = 0;
+ virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
// Get last release fence for the given layer
- virtual sp<Fence> getLayerReleaseFence(DisplayId, HWC2::Layer*) const = 0;
+ virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
// Set the output buffer and acquire fence for a virtual display.
- // Returns INVALID_OPERATION if displayId is not a virtual display.
- virtual status_t setOutputBuffer(DisplayId, const sp<Fence>& acquireFence,
+ virtual status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) = 0;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- virtual void clearReleaseFences(DisplayId) = 0;
+ virtual void clearReleaseFences(HalDisplayId) = 0;
// Fetches the HDR capabilities of the given display
- virtual status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) = 0;
+ virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
- virtual int32_t getSupportedPerFrameMetadata(DisplayId) const = 0;
+ virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
// Returns the available RenderIntent of the given display.
- virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId, ui::ColorMode) const = 0;
+ virtual std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const = 0;
- virtual mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) = 0;
+ virtual mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) = 0;
// Returns the attributes of the color sampling engine.
- virtual status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
+ virtual status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) = 0;
- virtual status_t setDisplayContentSamplingEnabled(DisplayId, bool enabled,
+ virtual status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled,
uint8_t componentMask,
uint64_t maxFrames) = 0;
- virtual status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
+ virtual status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) = 0;
// Sets the brightness of a display.
- virtual std::future<status_t> setDisplayBrightness(DisplayId, float brightness) = 0;
+ virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0;
// Events handling ---------------------------------------------------------
@@ -174,35 +174,35 @@
hal::Connection) = 0;
virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
- virtual void setVsyncEnabled(DisplayId, hal::Vsync enabled) = 0;
+ virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
- virtual nsecs_t getRefreshTimestamp(DisplayId) const = 0;
- virtual bool isConnected(DisplayId) const = 0;
+ virtual nsecs_t getRefreshTimestamp(PhysicalDisplayId) const = 0;
+ virtual bool isConnected(PhysicalDisplayId) const = 0;
// Non-const because it can update configMap inside of mDisplayData
virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
- DisplayId) const = 0;
+ PhysicalDisplayId) const = 0;
- virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const = 0;
- virtual int getActiveConfigIndex(DisplayId) const = 0;
+ virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
+ PhysicalDisplayId) const = 0;
+ virtual int getActiveConfigIndex(PhysicalDisplayId) const = 0;
- virtual std::vector<ui::ColorMode> getColorModes(DisplayId) const = 0;
+ virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0;
- virtual status_t setActiveColorMode(DisplayId, ui::ColorMode mode, ui::RenderIntent) = 0;
-
- virtual bool isUsingVrComposer() const = 0;
+ virtual status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode mode,
+ ui::RenderIntent) = 0;
// Composer 2.4
- virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
- virtual bool isVsyncPeriodSwitchSupported(DisplayId) const = 0;
- virtual nsecs_t getDisplayVsyncPeriod(DisplayId) const = 0;
+ virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
+ virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
+ virtual nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const = 0;
virtual status_t setActiveConfigWithConstraints(
- DisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
+ PhysicalDisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
- virtual status_t setAutoLowLatencyMode(DisplayId, bool on) = 0;
+ virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
virtual status_t getSupportedContentTypes(
- DisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
- virtual status_t setContentType(DisplayId, hal::ContentType) = 0;
+ PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+ virtual status_t setContentType(PhysicalDisplayId, hal::ContentType) = 0;
virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
const = 0;
@@ -234,7 +234,7 @@
DisplayIdentificationData* outData) const override;
bool hasCapability(hal::Capability) const override;
- bool hasDisplayCapability(DisplayId, hal::DisplayCapability) const override;
+ bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -244,63 +244,62 @@
void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
// Attempts to create a new layer on this display
- HWC2::Layer* createLayer(DisplayId) override;
+ HWC2::Layer* createLayer(HalDisplayId) override;
// Destroy a previously created layer
- void destroyLayer(DisplayId, HWC2::Layer*) override;
+ void destroyLayer(HalDisplayId, HWC2::Layer*) override;
status_t getDeviceCompositionChanges(
- DisplayId, bool frameUsesClientComposition,
+ HalDisplayId, bool frameUsesClientComposition,
std::optional<DeviceRequestedChanges>* outChanges) override;
- status_t setClientTarget(DisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+ status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& target, ui::Dataspace) override;
// Present layers to the display and read releaseFences.
- status_t presentAndGetReleaseFences(DisplayId) override;
+ status_t presentAndGetReleaseFences(HalDisplayId) override;
// set power mode
- status_t setPowerMode(DisplayId, hal::PowerMode mode) override;
+ status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
// Sets a color transform to be applied to the result of composition
- status_t setColorTransform(DisplayId, const mat4& transform) override;
+ status_t setColorTransform(HalDisplayId, const mat4& transform) override;
- // reset state when an external, non-virtual display is disconnected
- void disconnectDisplay(DisplayId) override;
+ // reset state when a display is disconnected
+ void disconnectDisplay(HalDisplayId) override;
// get the present fence received from the last call to present.
- sp<Fence> getPresentFence(DisplayId) const override;
+ sp<Fence> getPresentFence(HalDisplayId) const override;
// Get last release fence for the given layer
- sp<Fence> getLayerReleaseFence(DisplayId, HWC2::Layer*) const override;
+ sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
// Set the output buffer and acquire fence for a virtual display.
- // Returns INVALID_OPERATION if displayId is not a virtual display.
- status_t setOutputBuffer(DisplayId, const sp<Fence>& acquireFence,
+ status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
const sp<GraphicBuffer>& buffer) override;
// After SurfaceFlinger has retrieved the release fences for all the frames,
// it can call this to clear the shared pointers in the release fence map
- void clearReleaseFences(DisplayId) override;
+ void clearReleaseFences(HalDisplayId) override;
// Fetches the HDR capabilities of the given display
- status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) override;
+ status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
- int32_t getSupportedPerFrameMetadata(DisplayId) const override;
+ int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
// Returns the available RenderIntent of the given display.
- std::vector<ui::RenderIntent> getRenderIntents(DisplayId, ui::ColorMode) const override;
+ std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const override;
- mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) override;
+ mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) override;
// Returns the attributes of the color sampling engine.
- status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
+ status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
ui::Dataspace* outDataspace,
uint8_t* outComponentMask) override;
- status_t setDisplayContentSamplingEnabled(DisplayId, bool enabled, uint8_t componentMask,
+ status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled, uint8_t componentMask,
uint64_t maxFrames) override;
- status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
+ status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
DisplayedFrameStats* outStats) override;
- std::future<status_t> setDisplayBrightness(DisplayId, float brightness) override;
+ std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override;
// Events handling ---------------------------------------------------------
@@ -309,33 +308,32 @@
std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
- void setVsyncEnabled(DisplayId, hal::Vsync enabled) override;
+ void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
- nsecs_t getRefreshTimestamp(DisplayId) const override;
- bool isConnected(DisplayId) const override;
+ nsecs_t getRefreshTimestamp(PhysicalDisplayId) const override;
+ bool isConnected(PhysicalDisplayId) const override;
// Non-const because it can update configMap inside of mDisplayData
- std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(DisplayId) const override;
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
+ PhysicalDisplayId) const override;
- std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const override;
- int getActiveConfigIndex(DisplayId) const override;
+ std::shared_ptr<const HWC2::Display::Config> getActiveConfig(PhysicalDisplayId) const override;
+ int getActiveConfigIndex(PhysicalDisplayId) const override;
- std::vector<ui::ColorMode> getColorModes(DisplayId) const override;
+ std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override;
- status_t setActiveColorMode(DisplayId, ui::ColorMode, ui::RenderIntent) override;
-
- bool isUsingVrComposer() const override;
+ status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
// Composer 2.4
- DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
- bool isVsyncPeriodSwitchSupported(DisplayId) const override;
- nsecs_t getDisplayVsyncPeriod(DisplayId) const override;
- status_t setActiveConfigWithConstraints(DisplayId, size_t configId,
+ DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
+ bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
+ nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const override;
+ status_t setActiveConfigWithConstraints(PhysicalDisplayId, size_t configId,
const hal::VsyncPeriodChangeConstraints&,
hal::VsyncPeriodChangeTimeline* outTimeline) override;
- status_t setAutoLowLatencyMode(DisplayId, bool) override;
- status_t getSupportedContentTypes(DisplayId, std::vector<hal::ContentType>*) override;
- status_t setContentType(DisplayId, hal::ContentType) override;
+ status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
+ status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override;
+ status_t setContentType(PhysicalDisplayId, hal::ContentType) override;
const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
@@ -389,7 +387,7 @@
nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
};
- std::unordered_map<DisplayId, DisplayData> mDisplayData;
+ std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<hal::Capability> mCapabilities;
@@ -401,9 +399,7 @@
std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
bool mHasMultiDisplaySupport = false;
- std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
- uint32_t mNextVirtualDisplayId = 0;
- uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
+ RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()};
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index fba3261..247ee23 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -57,8 +57,7 @@
}
}
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
- const std::optional<DisplayId>& displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -125,7 +124,7 @@
}
status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -139,7 +138,7 @@
}
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -187,7 +186,7 @@
}
status_t VirtualDisplaySurface::advanceFrame() {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return NO_ERROR;
}
@@ -219,9 +218,11 @@
mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
+ const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+ LOG_FATAL_IF(!halDisplayId);
// At this point we know the output buffer acquire fence,
// so update HWC state with it.
- mHwc.setOutputBuffer(*mDisplayId, mOutputFence, outBuffer);
+ mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
@@ -230,7 +231,7 @@
mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN);
}
@@ -238,7 +239,8 @@
}
void VirtualDisplaySurface::onFrameCommitted() {
- if (!mDisplayId) {
+ const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+ if (!halDisplayId) {
return;
}
@@ -246,7 +248,7 @@
"Unexpected onFrameCommitted() in %s state", dbgStateStr());
mDbgState = DBG_STATE_IDLE;
- sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId);
+ sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
@@ -301,7 +303,7 @@
status_t VirtualDisplaySurface::requestBuffer(int pslot,
sp<GraphicBuffer>* outBuf) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
@@ -323,7 +325,7 @@
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
- LOG_FATAL_IF(!mDisplayId);
+ LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
status_t result =
mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -369,7 +371,7 @@
PixelFormat format, uint64_t usage,
uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
outTimestamps);
}
@@ -456,7 +458,7 @@
status_t VirtualDisplaySurface::queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
@@ -514,7 +516,7 @@
status_t VirtualDisplaySurface::cancelBuffer(int pslot,
const sp<Fence>& fence) {
- if (!mDisplayId) {
+ if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
@@ -626,7 +628,7 @@
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
- LOG_FATAL_IF(!mDisplayId);
+ LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
@@ -645,7 +647,9 @@
// until after GPU calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
- result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
+ const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+ LOG_FATAL_IF(!halDisplayId);
+ result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
mProducerBuffers[mOutputProducerSlot]);
return result;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 3cbad8f..1974625 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -24,6 +24,7 @@
#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
+#include <ui/DisplayId.h>
#include "DisplayIdentification.h"
@@ -77,8 +78,7 @@
public BnGraphicBufferProducer,
private ConsumerBase {
public:
- VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId,
- const sp<IGraphicBufferProducer>& sink,
+ VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
@@ -86,7 +86,7 @@
// DisplaySurface interface
//
virtual status_t beginFrame(bool mustRecompose);
- virtual status_t prepareFrame(CompositionType compositionType);
+ virtual status_t prepareFrame(CompositionType);
virtual status_t advanceFrame();
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
@@ -104,25 +104,22 @@
virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual status_t setAsyncMode(bool async);
- virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h,
- PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+ virtual status_t dequeueBuffer(int* pslot, sp<Fence>*, uint32_t w, uint32_t h, PixelFormat,
+ uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps);
virtual status_t detachBuffer(int slot);
- virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
- virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
- virtual status_t queueBuffer(int pslot,
- const QueueBufferInput& input, QueueBufferOutput* output);
- virtual status_t cancelBuffer(int pslot, const sp<Fence>& fence);
+ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+ virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>&);
+ virtual status_t queueBuffer(int pslot, const QueueBufferInput&, QueueBufferOutput*);
+ virtual status_t cancelBuffer(int pslot, const sp<Fence>&);
virtual int query(int what, int* value);
- virtual status_t connect(const sp<IProducerListener>& listener,
- int api, bool producerControlledByApp, QueueBufferOutput* output);
- virtual status_t disconnect(int api, DisconnectMode mode);
+ virtual status_t connect(const sp<IProducerListener>&, int api, bool producerControlledByApp,
+ QueueBufferOutput*);
+ virtual status_t disconnect(int api, DisconnectMode);
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
- virtual void allocateBuffers(uint32_t width, uint32_t height,
- PixelFormat format, uint64_t usage);
+ virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat, uint64_t usage);
virtual status_t allowAllocation(bool allow);
- virtual status_t setGenerationNumber(uint32_t generationNumber);
+ virtual status_t setGenerationNumber(uint32_t);
virtual String8 getConsumerName() const override;
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;
@@ -135,10 +132,9 @@
//
// Utility methods
//
- static Source fbSourceForCompositionType(CompositionType type);
- status_t dequeueBuffer(Source source, PixelFormat format, uint64_t usage,
- int* sslot, sp<Fence>* fence);
- void updateQueueBufferOutput(QueueBufferOutput&& qbo);
+ static Source fbSourceForCompositionType(CompositionType);
+ status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
+ void updateQueueBufferOutput(QueueBufferOutput&&);
void resetPerFrameState();
status_t refreshOutputBuffer();
@@ -148,14 +144,14 @@
// internally in the VirtualDisplaySurface. To minimize the number of times
// a producer slot switches which source it comes from, we map source slot
// numbers to producer slot numbers differently for each source.
- static int mapSource2ProducerSlot(Source source, int sslot);
- static int mapProducer2SourceSlot(Source source, int pslot);
+ static int mapSource2ProducerSlot(Source, int sslot);
+ static int mapProducer2SourceSlot(Source, int pslot);
//
// Immutable after construction
//
HWComposer& mHwc;
- const std::optional<DisplayId> mDisplayId;
+ const VirtualDisplayId mDisplayId;
const std::string mDisplayName;
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/DisplayIdGenerator.h b/services/surfaceflinger/DisplayIdGenerator.h
new file mode 100644
index 0000000..e7c69a8
--- /dev/null
+++ b/services/surfaceflinger/DisplayIdGenerator.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <ui/DisplayId.h>
+
+#include <limits>
+#include <optional>
+#include <random>
+#include <unordered_set>
+
+#include <log/log.h>
+
+namespace android {
+
+template <typename T>
+class DisplayIdGenerator {
+public:
+ virtual std::optional<T> nextId() = 0;
+ virtual void markUnused(T id) = 0;
+
+protected:
+ ~DisplayIdGenerator() {}
+};
+
+template <typename T>
+class RandomDisplayIdGenerator final : public DisplayIdGenerator<T> {
+public:
+ explicit RandomDisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max())
+ : mMaxIdsCount(maxIdsCount) {}
+
+ std::optional<T> nextId() override {
+ if (mUsedIds.size() >= mMaxIdsCount) {
+ return std::nullopt;
+ }
+
+ constexpr int kMaxAttempts = 1000;
+
+ for (int attempts = 0; attempts < kMaxAttempts; attempts++) {
+ const auto baseId = mDistribution(mGenerator);
+ const T id(baseId);
+ if (mUsedIds.count(id) == 0) {
+ mUsedIds.insert(id);
+ return id;
+ }
+ }
+
+ LOG_ALWAYS_FATAL("Couldn't generate ID after %d attempts", kMaxAttempts);
+ }
+
+ void markUnused(T id) override { mUsedIds.erase(id); }
+
+private:
+ const size_t mMaxIdsCount;
+
+ std::unordered_set<T> mUsedIds;
+ std::default_random_engine mGenerator{std::random_device()()};
+ std::uniform_int_distribution<typename T::BaseId> mDistribution;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
new file mode 100644
index 0000000..6ba4c43
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -0,0 +1,16 @@
+cc_library_static {
+ name: "libframetimeline",
+ defaults: ["surfaceflinger_defaults"],
+ srcs: [
+ "FrameTimeline.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libgui",
+ "libui",
+ "libutils",
+ ],
+ export_include_dirs: ["."],
+}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
new file mode 100644
index 0000000..43176a3
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -0,0 +1,548 @@
+/*
+ * 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 "FrameTimeline"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FrameTimeline.h"
+#include <android-base/stringprintf.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cinttypes>
+#include <numeric>
+
+namespace android::frametimeline::impl {
+
+using base::StringAppendF;
+
+void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
+ const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "\t\t");
+ StringAppendF(&result, " Start time\t\t|");
+ StringAppendF(&result, " End time\t\t|");
+ StringAppendF(&result, " Present time\n");
+ if (predictionState == PredictionState::Valid) {
+ // Dump the Predictions only if they are valid
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Expected\t|");
+ std::chrono::nanoseconds startTime(predictions.startTime - baseTime);
+ std::chrono::nanoseconds endTime(predictions.endTime - baseTime);
+ std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime);
+ StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n",
+ std::chrono::duration<double, std::milli>(startTime).count(),
+ std::chrono::duration<double, std::milli>(endTime).count(),
+ std::chrono::duration<double, std::milli>(presentTime).count());
+ }
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Actual \t|");
+
+ if (actuals.startTime == 0) {
+ StringAppendF(&result, "\t\tN/A\t|");
+ } else {
+ std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime));
+ StringAppendF(&result, "\t%10.2f\t|",
+ std::chrono::duration<double, std::milli>(startTime).count());
+ }
+ if (actuals.endTime == 0) {
+ StringAppendF(&result, "\t\tN/A\t|");
+ } else {
+ std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
+ StringAppendF(&result, "\t%10.2f\t|",
+ std::chrono::duration<double, std::milli>(endTime).count());
+ }
+ if (actuals.presentTime == 0) {
+ StringAppendF(&result, "\t\tN/A\n");
+ } else {
+ std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime));
+ StringAppendF(&result, "\t%10.2f\n",
+ std::chrono::duration<double, std::milli>(presentTime).count());
+ }
+
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------\n");
+}
+
+std::string toString(PredictionState predictionState) {
+ switch (predictionState) {
+ case PredictionState::Valid:
+ return "Valid";
+ case PredictionState::Expired:
+ return "Expired";
+ case PredictionState::None:
+ default:
+ return "None";
+ }
+}
+
+std::string toString(JankType jankType) {
+ switch (jankType) {
+ case JankType::None:
+ return "None";
+ case JankType::Display:
+ return "Composer/Display - outside SF and App";
+ case JankType::SurfaceFlingerDeadlineMissed:
+ return "SurfaceFlinger Deadline Missed";
+ case JankType::AppDeadlineMissed:
+ return "App Deadline Missed";
+ case JankType::PredictionExpired:
+ return "Prediction Expired";
+ case JankType::SurfaceFlingerEarlyLatch:
+ return "SurfaceFlinger Early Latch";
+ default:
+ return "Unclassified";
+ }
+}
+
+std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
+ std::vector<std::string> jankInfo;
+
+ if (jankMetadata & EarlyStart) {
+ jankInfo.emplace_back("Early Start");
+ } else if (jankMetadata & LateStart) {
+ jankInfo.emplace_back("Late Start");
+ }
+
+ if (jankMetadata & EarlyFinish) {
+ jankInfo.emplace_back("Early Finish");
+ } else if (jankMetadata & LateFinish) {
+ jankInfo.emplace_back("Late Finish");
+ }
+
+ if (jankMetadata & EarlyPresent) {
+ jankInfo.emplace_back("Early Present");
+ } else if (jankMetadata & LatePresent) {
+ jankInfo.emplace_back("Late Present");
+ }
+ // TODO(b/169876734): add GPU composition metadata here
+
+ if (jankInfo.empty()) {
+ return "None";
+ }
+ return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+ [](const std::string& l, const std::string& r) {
+ return l.empty() ? r : l + ", " + r;
+ });
+}
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ const int64_t assignedToken = mCurrentToken++;
+ mPredictions[assignedToken] = predictions;
+ mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
+ flushTokens(systemTime());
+ return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ flushTokens(systemTime());
+ auto predictionsIterator = mPredictions.find(token);
+ if (predictionsIterator != mPredictions.end()) {
+ return predictionsIterator->second;
+ }
+ return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+ for (size_t i = 0; i < mTokens.size(); i++) {
+ if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
+ mPredictions.erase(mTokens[i].first);
+ mTokens.erase(mTokens.begin() + static_cast<int>(i));
+ --i;
+ } else {
+ // Tokens are ordered by time. If i'th token is within the retention time, then the
+ // i+1'th token will also be within retention time.
+ break;
+ }
+ }
+}
+
+SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState,
+ frametimeline::TimelineItem&& predictions)
+ : mLayerName(layerName),
+ mPresentState(PresentState::Unknown),
+ mPredictionState(predictionState),
+ mPredictions(predictions),
+ mActuals({0, 0, 0}),
+ mActualQueueTime(0),
+ mJankType(JankType::None),
+ mJankMetadata(0) {}
+
+void SurfaceFrame::setPresentState(PresentState state) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPresentState = state;
+}
+
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPresentState;
+}
+
+TimelineItem SurfaceFrame::getActuals() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mActuals;
+}
+
+nsecs_t SurfaceFrame::getActualQueueTime() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mActualQueueTime;
+}
+
+void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals.startTime = actualStartTime;
+}
+
+void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActualQueueTime = actualQueueTime;
+}
+void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
+}
+
+void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals.presentTime = presentTime;
+}
+
+void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mJankType = jankType;
+ mJankMetadata = jankMetadata;
+}
+
+JankType SurfaceFrame::getJankType() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mJankType;
+}
+
+nsecs_t SurfaceFrame::getBaseTime() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+ if (mPredictionState == PredictionState::Valid) {
+ baseTime = std::min(baseTime, mPredictions.startTime);
+ }
+ if (mActuals.startTime != 0) {
+ baseTime = std::min(baseTime, mActuals.startTime);
+ }
+ baseTime = std::min(baseTime, mActuals.endTime);
+ return baseTime;
+}
+
+std::string presentStateToString(SurfaceFrame::PresentState presentState) {
+ using PresentState = SurfaceFrame::PresentState;
+ switch (presentState) {
+ case PresentState::Presented:
+ return "Presented";
+ case PresentState::Dropped:
+ return "Dropped";
+ case PresentState::Unknown:
+ default:
+ return "Unknown";
+ }
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer - %s", mLayerName.c_str());
+ if (mJankType != JankType::None) {
+ // Easily identify a janky Surface Frame in the dump
+ StringAppendF(&result, " [*] ");
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Jank Metadata: %s\n",
+ jankMetadataBitmaskToString(mJankMetadata).c_str());
+ dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
+}
+
+FrameTimeline::FrameTimeline()
+ : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+ mMaxDisplayFrames(kDefaultMaxDisplayFrames) {}
+
+FrameTimeline::DisplayFrame::DisplayFrame()
+ : surfaceFlingerPredictions(TimelineItem()),
+ surfaceFlingerActuals(TimelineItem()),
+ predictionState(PredictionState::None),
+ jankType(JankType::None),
+ jankMetadata(0) {
+ this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
+}
+
+std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+ const std::string& layerName, std::optional<int64_t> token) {
+ ATRACE_CALL();
+ if (!token) {
+ return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
+ TimelineItem());
+ }
+ std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
+ if (predictions) {
+ return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid,
+ std::move(*predictions));
+ }
+ return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired,
+ TimelineItem());
+}
+
+void FrameTimeline::addSurfaceFrame(
+ std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
+ SurfaceFrame::PresentState state) {
+ ATRACE_CALL();
+ surfaceFrame->setPresentState(state);
+ std::unique_ptr<impl::SurfaceFrame> implSurfaceFrame(
+ static_cast<impl::SurfaceFrame*>(surfaceFrame.release()));
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->surfaceFrames.push_back(std::move(implSurfaceFrame));
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+ ATRACE_CALL();
+ const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!prediction) {
+ mCurrentDisplayFrame->predictionState = PredictionState::Expired;
+ } else {
+ mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
+ mCurrentDisplayFrame->predictionState = PredictionState::Valid;
+ }
+ mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
+ const std::shared_ptr<FenceTime>& presentFence) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+ mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
+ flushPendingPresentFences();
+ finalizeCurrentDisplayFrame();
+}
+
+void FrameTimeline::flushPendingPresentFences() {
+ 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;
+ }
+ }
+ if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+ auto& displayFrame = pendingPresentFence.second;
+ displayFrame->surfaceFlingerActuals.presentTime = signalTime;
+
+ // Jank Analysis for DisplayFrame
+ const auto& sfActuals = displayFrame->surfaceFlingerActuals;
+ const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
+ if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
+ displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
+ ? LatePresent
+ : EarlyPresent;
+ }
+ if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
+ if (sfActuals.endTime > sfPredictions.endTime) {
+ displayFrame->jankMetadata |= LateFinish;
+ } else {
+ displayFrame->jankMetadata |= EarlyFinish;
+ }
+
+ if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) {
+ displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
+ } else if (displayFrame->jankMetadata & LateFinish & LatePresent) {
+ displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
+ } else if (displayFrame->jankMetadata & EarlyPresent ||
+ displayFrame->jankMetadata & LatePresent) {
+ // Cases where SF finished early but frame was presented late and vice versa
+ displayFrame->jankType = JankType::Display;
+ }
+ }
+ if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
+ displayFrame->jankMetadata |=
+ sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
+ }
+
+ for (auto& surfaceFrame : displayFrame->surfaceFrames) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
+ // Only presented SurfaceFrames need to be updated
+ surfaceFrame->setActualPresentTime(signalTime);
+
+ // Jank Analysis for SurfaceFrame
+ const auto& predictionState = surfaceFrame->getPredictionState();
+ if (predictionState == PredictionState::Expired) {
+ // Jank analysis cannot be done on apps that don't use predictions
+ surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
+ continue;
+ } else if (predictionState == PredictionState::Valid) {
+ const auto& actuals = surfaceFrame->getActuals();
+ const auto& predictions = surfaceFrame->getPredictions();
+ int32_t jankMetadata = 0;
+ JankType jankType = JankType::None;
+ if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
+ jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
+ : EarlyFinish;
+ }
+ if (std::abs(actuals.presentTime - predictions.presentTime) >
+ kPresentThreshold) {
+ jankMetadata |= actuals.presentTime > predictions.presentTime
+ ? LatePresent
+ : EarlyPresent;
+ }
+ if (jankMetadata & EarlyPresent) {
+ jankType = JankType::SurfaceFlingerEarlyLatch;
+ } else if (jankMetadata & LatePresent) {
+ if (jankMetadata & EarlyFinish) {
+ // TODO(b/169890654): Classify this properly
+ jankType = JankType::Display;
+ } else {
+ jankType = JankType::AppDeadlineMissed;
+ }
+ }
+ surfaceFrame->setJankInfo(jankType, jankMetadata);
+ }
+ }
+ }
+ }
+
+ mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
+ --i;
+ }
+}
+
+void FrameTimeline::finalizeCurrentDisplayFrame() {
+ while (mDisplayFrames.size() >= mMaxDisplayFrames) {
+ // We maintain only a fixed number of frames' data. Pop older frames
+ mDisplayFrames.pop_front();
+ }
+ mDisplayFrames.push_back(mCurrentDisplayFrame);
+ mCurrentDisplayFrame.reset();
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+}
+
+nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
+ nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+ if (displayFrame->predictionState == PredictionState::Valid) {
+ baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
+ }
+ baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
+ for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
+ if (surfaceFrameBaseTime != 0) {
+ baseTime = std::min(baseTime, surfaceFrameBaseTime);
+ }
+ }
+ return baseTime;
+}
+
+void FrameTimeline::dumpDisplayFrame(std::string& result,
+ const std::shared_ptr<DisplayFrame>& displayFrame,
+ nsecs_t baseTime) {
+ if (displayFrame->jankType != JankType::None) {
+ // Easily identify a janky Display Frame in the dump
+ StringAppendF(&result, " [*] ");
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "Prediction State : %s\n",
+ toString(displayFrame->predictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
+ StringAppendF(&result, "Jank Metadata: %s\n",
+ jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
+ dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
+ "", displayFrame->predictionState, baseTime);
+ StringAppendF(&result, "\n");
+ std::string indent = " "; // 4 spaces
+ for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ surfaceFrame->dump(result, indent, baseTime);
+ }
+ StringAppendF(&result, "\n");
+}
+void FrameTimeline::dumpAll(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+ dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+ }
+}
+
+void FrameTimeline::dumpJank(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ const auto& displayFrame = mDisplayFrames[i];
+ if (displayFrame->jankType == JankType::None) {
+ // Check if any Surface Frame has been janky
+ bool isJanky = false;
+ for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ if (surfaceFrame->getJankType() != JankType::None) {
+ isJanky = true;
+ break;
+ }
+ }
+ if (!isJanky) {
+ continue;
+ }
+ }
+ StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+ dumpDisplayFrame(result, displayFrame, baseTime);
+ }
+}
+void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
+ ATRACE_CALL();
+ std::unordered_map<std::string, bool> argsMap;
+ for (size_t i = 0; i < args.size(); i++) {
+ argsMap[std::string(String8(args[i]).c_str())] = true;
+ }
+ if (argsMap.count("-jank")) {
+ dumpJank(result);
+ }
+ if (argsMap.count("-all")) {
+ dumpAll(result);
+ }
+}
+
+void FrameTimeline::setMaxDisplayFrames(uint32_t size) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // The size can either increase or decrease, clear everything, to be consistent
+ mDisplayFrames.clear();
+ mPendingPresentFences.clear();
+ mMaxDisplayFrames = size;
+}
+
+void FrameTimeline::reset() {
+ setMaxDisplayFrames(kDefaultMaxDisplayFrames);
+}
+
+} // namespace android::frametimeline::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
new file mode 100644
index 0000000..bd637df
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -0,0 +1,320 @@
+/*
+ * 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 <deque>
+#include <mutex>
+
+#include <gui/ISurfaceComposer.h>
+#include <ui/FenceTime.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+namespace android::frametimeline {
+
+/*
+ * The type of jank that is associated with a Display/Surface frame
+ */
+enum class JankType {
+ // No Jank
+ None,
+ // Jank not related to SurfaceFlinger or the App
+ Display,
+ // SF took too long on the CPU
+ SurfaceFlingerDeadlineMissed,
+ // Either App or GPU took too long on the frame
+ AppDeadlineMissed,
+ // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank
+ // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+ PredictionExpired,
+ // Latching a buffer early might cause an early present of the frame
+ SurfaceFlingerEarlyLatch,
+};
+
+enum JankMetadata {
+ // Frame was presented earlier than expected
+ EarlyPresent = 0x1,
+ // Frame was presented later than expected
+ LatePresent = 0x2,
+ // App/SF started earlier than expected
+ EarlyStart = 0x4,
+ // App/SF started later than expected
+ LateStart = 0x8,
+ // App/SF finished work earlier than the deadline
+ EarlyFinish = 0x10,
+ // App/SF finished work later than the deadline
+ LateFinish = 0x20,
+ // SF was in GPU composition
+ GpuComposition = 0x40,
+};
+
+class FrameTimelineTest;
+
+/*
+ * Collection of timestamps that can be used for both predictions and actual times.
+ */
+struct TimelineItem {
+ TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
+ const nsecs_t presentTime = 0)
+ : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
+
+ nsecs_t startTime;
+ nsecs_t endTime;
+ nsecs_t presentTime;
+
+ bool operator==(const TimelineItem& other) const {
+ return startTime == other.startTime && endTime == other.endTime &&
+ presentTime == other.presentTime;
+ }
+
+ bool operator!=(const TimelineItem& other) const { return !(*this == other); }
+};
+
+/*
+ * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
+ * saves these predictions for a short period of time and returns the predictions for a given token,
+ * if it hasn't expired.
+ */
+class TokenManager {
+public:
+ virtual ~TokenManager() = default;
+
+ // Generates a token for the given set of predictions. Stores the predictions for 120ms and
+ // destroys it later.
+ virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
+};
+
+enum class PredictionState {
+ Valid, // Predictions obtained successfully from the TokenManager
+ Expired, // TokenManager no longer has the predictions
+ None, // Predictions are either not present or didn't come from TokenManager
+};
+
+/*
+ * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame
+ * from the app
+ */
+class SurfaceFrame {
+public:
+ enum class PresentState {
+ Presented, // Buffer was latched and presented by SurfaceFlinger
+ Dropped, // Buffer was dropped by SurfaceFlinger
+ Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet
+ };
+
+ virtual ~SurfaceFrame() = default;
+
+ virtual TimelineItem getPredictions() const = 0;
+ virtual TimelineItem getActuals() const = 0;
+ virtual nsecs_t getActualQueueTime() const = 0;
+ virtual PresentState getPresentState() const = 0;
+ virtual PredictionState getPredictionState() const = 0;
+
+ virtual void setPresentState(PresentState state) = 0;
+
+ // Actual timestamps of the app are set individually at different functions.
+ // Start time (if the app provides) and Queue time are accessible after queueing the frame,
+ // whereas Acquire Fence time is available only during latch.
+ virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
+ virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
+ virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+};
+
+/*
+ * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were
+ * presented
+ */
+class FrameTimeline {
+public:
+ virtual ~FrameTimeline() = default;
+ virtual TokenManager* getTokenManager() = 0;
+
+ // Create a new surface frame, set the predictions based on a token and return it to the caller.
+ // Sets the PredictionState of SurfaceFrame.
+ virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
+ const std::string& layerName, std::optional<int64_t> token) = 0;
+
+ // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
+ // composited into one display frame.
+ virtual void addSurfaceFrame(std::unique_ptr<SurfaceFrame> surfaceFrame,
+ SurfaceFrame::PresentState state) = 0;
+
+ // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
+ // the token and sets the actualSfWakeTime for the current DisplayFrame.
+ virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0;
+
+ // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence
+ // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in
+ // that vsync.
+ virtual void setSfPresent(nsecs_t sfPresentTime,
+ const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+ // Args:
+ // -jank : Dumps only the Display Frames that are either janky themselves
+ // or contain janky Surface Frames.
+ // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within
+ virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0;
+
+ // Sets the max number of display frames that can be stored. Called by SF backdoor.
+ virtual void setMaxDisplayFrames(uint32_t size);
+
+ // Restores the max number of display frames to default. Called by SF backdoor.
+ virtual void reset() = 0;
+};
+
+namespace impl {
+
+using namespace std::chrono_literals;
+
+class TokenManager : public android::frametimeline::TokenManager {
+public:
+ TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
+ ~TokenManager() = default;
+
+ int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
+ std::optional<TimelineItem> getPredictionsForToken(int64_t token);
+
+private:
+ // Friend class for testing
+ friend class android::frametimeline::FrameTimelineTest;
+
+ void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
+
+ std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
+ std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex);
+ int64_t mCurrentToken GUARDED_BY(mMutex);
+ std::mutex mMutex;
+ static constexpr nsecs_t kMaxRetentionTime =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
+};
+
+class SurfaceFrame : public android::frametimeline::SurfaceFrame {
+public:
+ SurfaceFrame(const std::string& layerName, PredictionState predictionState,
+ TimelineItem&& predictions);
+ ~SurfaceFrame() = default;
+
+ TimelineItem getPredictions() const override { return mPredictions; };
+ TimelineItem getActuals() const override;
+ nsecs_t getActualQueueTime() const override;
+ PresentState getPresentState() const override;
+ PredictionState getPredictionState() const override { return mPredictionState; };
+
+ void setActualStartTime(nsecs_t actualStartTime) override;
+ void setActualQueueTime(nsecs_t actualQueueTime) override;
+ void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
+ void setPresentState(PresentState state) override;
+ void setActualPresentTime(nsecs_t presentTime);
+ void setJankInfo(JankType jankType, int32_t jankMetadata);
+ JankType getJankType() const;
+ nsecs_t getBaseTime() const;
+ // All the timestamps are dumped relative to the baseTime
+ void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
+
+private:
+ const std::string mLayerName;
+ PresentState mPresentState GUARDED_BY(mMutex);
+ const PredictionState mPredictionState;
+ const TimelineItem mPredictions;
+ TimelineItem mActuals GUARDED_BY(mMutex);
+ nsecs_t mActualQueueTime GUARDED_BY(mMutex);
+ mutable std::mutex mMutex;
+ JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
+ int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
+};
+
+class FrameTimeline : public android::frametimeline::FrameTimeline {
+public:
+ FrameTimeline();
+ ~FrameTimeline() = default;
+
+ frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
+ std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
+ const std::string& layerName, std::optional<int64_t> token) override;
+ void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
+ SurfaceFrame::PresentState state) override;
+ void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
+ void setSfPresent(nsecs_t sfPresentTime,
+ const std::shared_ptr<FenceTime>& presentFence) override;
+ void parseArgs(const Vector<String16>& args, std::string& result) override;
+ void setMaxDisplayFrames(uint32_t size) override;
+ void reset() override;
+
+private:
+ // Friend class for testing
+ friend class android::frametimeline::FrameTimelineTest;
+
+ /*
+ * DisplayFrame should be used only internally within FrameTimeline.
+ */
+ struct DisplayFrame {
+ DisplayFrame();
+
+ /* Usage of TimelineItem w.r.t SurfaceFlinger
+ * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates
+ * endTime Time when SurfaceFlinger sends a composited frame to Display
+ * presentTime Time when the composited frame was presented on screen
+ */
+ TimelineItem surfaceFlingerPredictions;
+ TimelineItem surfaceFlingerActuals;
+
+ // Collection of predictions and actual values sent over by Layers
+ std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
+
+ PredictionState predictionState;
+ JankType jankType = JankType::None; // Enum for the type of jank
+ int32_t jankMetadata = 0x0; // Additional details about the jank
+ };
+
+ void flushPendingPresentFences() REQUIRES(mMutex);
+ void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+ // BaseTime is the smallest timestamp in a DisplayFrame.
+ // Used for dumping all timestamps relative to the oldest, making it easy to read.
+ nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
+ void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
+ nsecs_t baseTime) REQUIRES(mMutex);
+ void dumpAll(std::string& result);
+ void dumpJank(std::string& result);
+
+ // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
+ std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
+ std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
+ mPendingPresentFences GUARDED_BY(mMutex);
+ std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
+ TokenManager mTokenManager;
+ std::mutex mMutex;
+ uint32_t mMaxDisplayFrames;
+ static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
+ // The initial container size for the vector<SurfaceFrames> inside display frame. Although this
+ // number doesn't represent any bounds on the number of surface frames that can go in a display
+ // frame, this is a good starting size for the vector so that we can avoid the internal vector
+ // resizing that happens with push_back.
+ static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+ // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+ // compared to prediction, we don't treat it as a jank.
+ static constexpr nsecs_t kPresentThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ static constexpr nsecs_t kDeadlineThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ static constexpr nsecs_t kSFStartThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
+};
+
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 138d08c..d9faec4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -62,6 +62,7 @@
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
#include "EffectLayer.h"
+#include "FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "LayerProtoHelper.h"
#include "LayerRejecter.h"
@@ -76,6 +77,7 @@
using base::StringAppendF;
using namespace android::flag_operators;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
std::atomic<int32_t> Layer::sSequence{1};
@@ -89,6 +91,8 @@
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
+ if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
+ layerFlags |= layer_state_t::eLayerSkipScreenshot;
mCurrentState.active_legacy.w = args.w;
mCurrentState.active_legacy.h = args.h;
@@ -122,6 +126,8 @@
mCurrentState.shadowRadius = 0.f;
mCurrentState.treeHasFrameRateVote = false;
mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+ mCurrentState.frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+ mCurrentState.postTime = -1;
if (args.flags & ISurfaceComposerClient::eNoColorFill) {
// Set an invalid color so there is no color fill.
@@ -504,9 +510,6 @@
compositionState->geomUsesSourceCrop = usesSourceCrop();
compositionState->isSecure = isSecure();
- compositionState->type = type;
- compositionState->appId = appId;
-
compositionState->metadata.clear();
const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
for (const auto& [key, mandatory] : supportedMetadata) {
@@ -775,7 +778,12 @@
bool Layer::isSecure() const {
const State& s(mDrawingState);
- return (s.flags & layer_state_t::eLayerSecure);
+ if (s.flags & layer_state_t::eLayerSecure) {
+ return true;
+ }
+
+ const auto p = mDrawingParent.promote();
+ return (p != nullptr) ? p->isSecure() : false;
}
// ----------------------------------------------------------------------------
@@ -801,10 +809,10 @@
// to be applied as per normal (no synchronization).
mCurrentState.barrierLayer_legacy = nullptr;
} else {
- auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy, this);
+ auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.barrierFrameNumber, this);
if (barrierLayer->addSyncPoint(syncPoint)) {
std::stringstream ss;
- ss << "Adding sync point " << mCurrentState.frameNumber_legacy;
+ ss << "Adding sync point " << mCurrentState.barrierFrameNumber;
ATRACE_NAME(ss.str().c_str());
mRemoteSyncPoints.push_back(std::move(syncPoint));
} else {
@@ -824,8 +832,8 @@
void Layer::popPendingState(State* stateToCommit) {
ATRACE_CALL();
- *stateToCommit = mPendingStates[0];
+ *stateToCommit = mPendingStates[0];
mPendingStates.removeAt(0);
ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
}
@@ -845,7 +853,7 @@
}
if (mRemoteSyncPoints.front()->getFrameNumber() !=
- mPendingStates[0].frameNumber_legacy) {
+ mPendingStates[0].barrierFrameNumber) {
ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
// Signal our end of the sync point and then dispose of it
@@ -882,6 +890,23 @@
mFlinger->setTraversalNeeded();
}
+ if (stateUpdateAvailable) {
+ const auto vsyncId =
+ stateToCommit->frameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID
+ ? std::nullopt
+ : std::make_optional(stateToCommit->frameTimelineVsyncId);
+
+ auto surfaceFrame =
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mTransactionName, vsyncId);
+ surfaceFrame->setActualQueueTime(stateToCommit->postTime);
+ // For transactions we set the acquire fence time to the post time as we
+ // don't have a buffer. For BufferStateLayer it is overridden in
+ // BufferStateLayer::applyPendingStates
+ surfaceFrame->setAcquireFenceTime(stateToCommit->postTime);
+
+ mSurfaceFrame = std::move(surfaceFrame);
+ }
+
mCurrentState.modified = false;
return stateUpdateAvailable;
}
@@ -1001,8 +1026,7 @@
this->contentDirty = true;
// we may use linear filtering, if the matrix scales us
- const uint8_t type = getActiveTransform(c).getType();
- mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE);
+ mNeedsFiltering = getActiveTransform(c).needsBilinearFiltering();
}
if (mCurrentState.inputInfoChanged) {
@@ -1020,6 +1044,7 @@
void Layer::commitTransaction(const State& stateToCommit) {
mDrawingState = stateToCommit;
+ mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mSurfaceFrame), PresentState::Presented);
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
@@ -1436,6 +1461,14 @@
return true;
}
+void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime) {
+ mCurrentState.sequence++;
+ mCurrentState.frameTimelineVsyncId = frameTimelineVsyncId;
+ mCurrentState.postTime = postTime;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+}
+
Layer::FrameRate Layer::getFrameRateForLayerTree() const {
const auto frameRate = getDrawingState().frameRate;
if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
@@ -1453,14 +1486,21 @@
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
ATRACE_CALL();
+ if (mLayerDetached) {
+ // If the layer is detached, then we don't defer this transaction since we will not
+ // commit the pending state while the layer is detached. Adding sync points may cause
+ // the barrier layer to wait for the states to be committed before dequeuing a buffer.
+ return;
+ }
+
mCurrentState.barrierLayer_legacy = barrierLayer;
- mCurrentState.frameNumber_legacy = frameNumber;
+ mCurrentState.barrierFrameNumber = frameNumber;
// We don't set eTransactionNeeded, because just receiving a deferral
// request without any other state updates shouldn't actually induce a delay
mCurrentState.modified = true;
pushPendingState();
mCurrentState.barrierLayer_legacy = nullptr;
- mCurrentState.frameNumber_legacy = 0;
+ mCurrentState.barrierFrameNumber = 0;
mCurrentState.modified = false;
}
@@ -2225,7 +2265,7 @@
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
const DisplayDevice* display) {
- ui::Transform transform = getTransform();
+ const ui::Transform transform = getTransform();
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
for (const auto& pendingState : mPendingStatesSnapshot) {
@@ -2233,7 +2273,7 @@
if (barrierLayer != nullptr) {
BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer();
barrierLayerProto->set_id(barrierLayer->sequence);
- barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy);
+ barrierLayerProto->set_frame_number(pendingState.barrierFrameNumber);
}
}
@@ -2409,32 +2449,64 @@
const float xScale = t.getScaleX();
const float yScale = t.getScaleY();
if (xScale != 1.0f || yScale != 1.0f) {
- info.touchableRegion.scaleSelf(xScale, yScale);
xSurfaceInset = std::round(xSurfaceInset * xScale);
ySurfaceInset = std::round(ySurfaceInset * yScale);
}
- layerBounds = t.transform(layerBounds);
+ Rect transformedLayerBounds = t.transform(layerBounds);
// clamp inset to layer bounds
- xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0;
- ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0;
+ xSurfaceInset = (xSurfaceInset >= 0)
+ ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2)
+ : 0;
+ ySurfaceInset = (ySurfaceInset >= 0)
+ ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2)
+ : 0;
- layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset);
+ // inset while protecting from overflow TODO(b/161235021): What is going wrong
+ // in the overflow scenario?
+ {
+ int32_t tmp;
+ if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp))
+ transformedLayerBounds.left = tmp;
+ if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp))
+ transformedLayerBounds.right = tmp;
+ if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp))
+ transformedLayerBounds.top = tmp;
+ if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp))
+ transformedLayerBounds.bottom = tmp;
+ }
// Input coordinate should match the layer bounds.
- info.frameLeft = layerBounds.left;
- info.frameTop = layerBounds.top;
- info.frameRight = layerBounds.right;
- info.frameBottom = layerBounds.bottom;
+ info.frameLeft = transformedLayerBounds.left;
+ info.frameTop = transformedLayerBounds.top;
+ info.frameRight = transformedLayerBounds.right;
+ info.frameBottom = transformedLayerBounds.bottom;
+ // Compute the correct transform to send to input. This will allow it to transform the
+ // input coordinates from display space into window space. Therefore, it needs to use the
+ // final layer frame to create the inverse transform. Since surface insets are added later,
+ // along with the overflow, the best way to ensure we get the correct transform is to use
+ // the final frame calculated.
+ // 1. Take the original transform set on the window and get the inverse transform. This is
+ // used to get the final bounds in display space (ignorning the transform). Apply the
+ // inverse transform on the layerBounds to get the untransformed frame (in display space)
+ // 2. Take the top and left of the untransformed frame to get the real position on screen.
+ // Apply the layer transform on top/left so it includes any scale or rotation. These will
+ // be the new translation values for the transform.
+ // 3. Update the translation of the original transform to the new translation values.
+ // 4. Send the inverse transform to input so the coordinates can be transformed back into
+ // window space.
+ ui::Transform inverseTransform = t.inverse();
+ Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds);
+ vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top);
ui::Transform inputTransform(t);
- inputTransform.set(layerBounds.left, layerBounds.top);
+ inputTransform.set(translation.x, translation.y);
info.transform = inputTransform.inverse();
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
- info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
+ info.touchableRegion = inputTransform.transform(info.touchableRegion);
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
@@ -2444,6 +2516,7 @@
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
+ info.alpha = getAlpha();
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
@@ -2643,6 +2716,16 @@
}
}
+bool Layer::getPrimaryDisplayOnly() const {
+ const State& s(mDrawingState);
+ if (s.flags & layer_state_t::eLayerSkipScreenshot) {
+ return true;
+ }
+
+ sp<Layer> parent = mDrawingParent.promote();
+ return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 521659d..02593d5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -55,8 +55,6 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Client;
class Colorizer;
class DisplayDevice;
@@ -73,7 +71,9 @@
class SurfaceInterceptor;
}
-// ---------------------------------------------------------------------------
+namespace frametimeline {
+class SurfaceFrame;
+} // namespace frametimeline
struct LayerCreationArgs {
LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
@@ -106,14 +106,6 @@
static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
public:
- mutable bool contentDirty{false};
- Region surfaceDamageRegion;
-
- // 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.
- int32_t sequence{sSequence++};
-
enum { // flags for doTransaction()
eDontUpdateGeometryState = 0x00000001,
eVisibleRegion = 0x00000002,
@@ -199,7 +191,7 @@
// If set, defers this state update until the identified Layer
// receives a frame with the given frameNumber
wp<Layer> barrierLayer_legacy;
- uint64_t frameNumber_legacy;
+ uint64_t barrierFrameNumber;
// the transparentRegion hint is a bit special, it's latched only
// when we receive a buffer -- this is because it's "content"
@@ -279,19 +271,64 @@
// a buffer of a different size. ui::Transform::ROT_INVALID means the
// a fixed transform hint is not set.
ui::Transform::RotationFlags fixedTransformHint;
+
+ // The vsync id that was used to start the transaction
+ int64_t frameTimelineVsyncId;
+
+ // When the transaction was posted
+ nsecs_t postTime;
+ };
+
+ /*
+ * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
+ * is called.
+ */
+ class LayerCleaner {
+ sp<SurfaceFlinger> mFlinger;
+ sp<Layer> mLayer;
+
+ protected:
+ ~LayerCleaner() {
+ // destroy client resources
+ mFlinger->onHandleDestroyed(mLayer);
+ }
+
+ public:
+ LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
+ : mFlinger(flinger), mLayer(layer) {}
+ };
+
+ /*
+ * 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), owner(layer) {}
+
+ wp<Layer> owner;
};
explicit Layer(const LayerCreationArgs& args);
virtual ~Layer();
- void onFirstRef() override;
+ static bool isLayerFocusedBasedOnPriority(int32_t priority);
+ static void miniDumpHeader(std::string& result);
+ static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
- InputWindowInfo::Type getWindowType() const { return mWindowType; }
+ // Provide unique string for each class type in the Layer hierarchy
+ virtual const char* getType() const = 0;
- void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
- bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
+ // true if this layer is visible, false otherwise
+ virtual bool isVisible() const = 0;
- // ------------------------------------------------------------------------
+ virtual sp<Layer> createClone() = 0;
+
// Geometry setting functions.
//
// The following group of functions are used to specify the layers
@@ -364,13 +401,9 @@
virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
virtual bool setMetadata(const LayerMetadata& data);
- bool reparentChildren(const sp<IBinder>& newParentHandle);
- void reparentChildren(const sp<Layer>& newParent);
virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
virtual bool detachChildren();
- bool attachChildren();
- bool isLayerDetached() const { return mLayerDetached; }
virtual bool setColorTransform(const mat4& matrix);
virtual mat4 getColorTransform() const;
virtual bool hasColorTransform() const;
@@ -383,7 +416,7 @@
virtual bool setFrame(const Rect& /*frame*/) { return false; };
virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
- const client_cache_t& /*clientCacheId*/) {
+ const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) {
return false;
};
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -403,23 +436,13 @@
}
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
- bool setShadowRadius(float shadowRadius);
virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
// If the variable is not set on the layer, it traverses up the tree to inherit the frame
// rate priority from its parent.
virtual int32_t getFrameRateSelectionPriority() const;
- static bool isLayerFocusedBasedOnPriority(int32_t priority);
-
virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
- // Before color management is introduced, contents on Android have to be
- // desaturated in order to match what they appears like visually.
- // With color management, these contents will appear desaturated, thus
- // needed to be saturated so that they match what they are designed for
- // visually.
- bool isLegacyDataSpace() const;
-
virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
virtual compositionengine::LayerFECompositionState* editCompositionState();
@@ -429,6 +452,196 @@
virtual void useSurfaceDamage() {}
virtual void useEmptyDamage() {}
+ /*
+ * isOpaque - true if this surface is opaque
+ *
+ * This takes into account the buffer format (i.e. whether or not the
+ * pixel format includes an alpha channel) and the "opaque" flag set
+ * on the layer. It does not examine the current plane alpha value.
+ */
+ virtual bool isOpaque(const Layer::State&) const { return false; }
+
+ /*
+ * Returns whether this layer can receive input.
+ */
+ virtual bool canReceiveInput() const;
+
+ /*
+ * isProtected - true if the layer may contain protected content in the
+ * GRALLOC_USAGE_PROTECTED sense.
+ */
+ virtual bool isProtected() const { return false; }
+
+ /*
+ * isFixedSize - true if content has a fixed size
+ */
+ virtual bool isFixedSize() const { return true; }
+
+ /*
+ * usesSourceCrop - true if content should use a source crop
+ */
+ virtual bool usesSourceCrop() const { return false; }
+
+ // Most layers aren't created from the main thread, and therefore need to
+ // grab the SF state lock to access HWC, but ContainerLayer does, so we need
+ // to avoid grabbing the lock again to avoid deadlock
+ virtual bool isCreatedFromMainThread() const { return false; }
+
+ virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
+ virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
+ virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
+ virtual ui::Transform getActiveTransform(const Layer::State& s) const {
+ return s.active_legacy.transform;
+ }
+ virtual Region getActiveTransparentRegion(const Layer::State& s) const {
+ return s.activeTransparentRegion_legacy;
+ }
+ virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+ virtual bool needsFiltering(const DisplayDevice*) const { return false; }
+
+ // True if this layer requires filtering
+ // This method is distinct from needsFiltering() in how the filter
+ // requirement is computed. needsFiltering() compares displayFrame and crop,
+ // where as this method transforms the displayFrame to layer-stack space
+ // first. This method should be used if there is no physical display to
+ // project onto when taking screenshots, as the filtering requirements are
+ // different.
+ // If the parent transform needs to be undone when capturing the layer, then
+ // the inverse parent transform is also required.
+ virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
+ return false;
+ }
+
+ virtual void updateCloneBufferInfo(){};
+
+ virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
+
+ virtual bool isHdrY410() const { return false; }
+
+ virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
+
+ virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; }
+
+ /*
+ * called after composition.
+ * returns true if the layer latched a new buffer this frame.
+ */
+ virtual bool onPostComposition(const DisplayDevice*,
+ const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+ const std::shared_ptr<FenceTime>& /*presentFence*/,
+ const CompositorTiming&) {
+ return false;
+ }
+
+ // If a buffer was replaced this frame, release the former buffer
+ virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
+
+ virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+ const CompositorTiming& /*compositorTiming*/) {}
+
+ /*
+ * latchBuffer - called each time the screen is redrawn and returns whether
+ * the visible regions need to be recomputed (this is a fairly heavy
+ * operation, so this should be set only if needed). Typically this is used
+ * to figure out if the content or size of a surface has changed.
+ */
+ virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+ nsecs_t /*expectedPresentTime*/) {
+ return false;
+ }
+
+ virtual bool isBufferLatched() const { return false; }
+
+ virtual void latchAndReleaseBuffer() {}
+
+ /*
+ * returns the rectangle that crops the content of the layer and scales it
+ * to the layer's size.
+ */
+ virtual Rect getBufferCrop() const { return Rect(); }
+
+ /*
+ * Returns the transform applied to the buffer.
+ */
+ virtual uint32_t getBufferTransform() const { return 0; }
+
+ virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+
+ virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+
+ /*
+ * Returns if a frame is ready
+ */
+ virtual bool hasReadyFrame() const { return false; }
+
+ virtual int32_t getQueuedFrameCount() const { return 0; }
+
+ virtual void pushPendingState();
+
+ /**
+ * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+ * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+ */
+ virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+
+ /**
+ * Returns the source bounds. If the bounds are not defined, it is inferred from the
+ * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
+ * For the root layer, this is the display viewport size.
+ */
+ virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
+ return parentBounds;
+ }
+ virtual FrameRate getFrameRateForLayerTree() const;
+
+ virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
+ return {};
+ }
+
+ virtual bool getTransformToDisplayInverse() const { return false; }
+
+ // Returns how rounded corners should be drawn for this layer.
+ // This will traverse the hierarchy until it reaches its root, finding topmost rounded
+ // corner definition and converting it into current layer's coordinates.
+ // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+ // ignored.
+ virtual RoundedCornerState getRoundedCornerState() const;
+
+ virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
+ virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
+ /**
+ * Return whether this layer needs an input info. For most layer types
+ * this is only true if they explicitly set an input-info but BufferLayer
+ * overrides this so we can generate input-info for Buffered layers that don't
+ * have them (for input occlusion detection checks).
+ */
+ virtual bool needsInputInfo() const { return hasInputInfo(); }
+
+ // Implements RefBase.
+ void onFirstRef() override;
+
+ // implements compositionengine::LayerFE
+ const compositionengine::LayerFECompositionState* getCompositionState() const override;
+ bool onPreComposition(nsecs_t) override;
+ void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
+ std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+ void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ const char* getDebugName() const override;
+
+ bool reparentChildren(const sp<IBinder>& newParentHandle);
+ void reparentChildren(const sp<Layer>& newParent);
+ bool attachChildren();
+ bool isLayerDetached() const { return mLayerDetached; }
+ bool setShadowRadius(float shadowRadius);
+
+ // Before color management is introduced, contents on Android have to be
+ // desaturated in order to match what they appears like visually.
+ // With color management, these contents will appear desaturated, thus
+ // needed to be saturated so that they match what they are designed for
+ // visually.
+ bool isLegacyDataSpace() const;
+
uint32_t getTransactionFlags() const { return mTransactionFlags; }
uint32_t getTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags);
@@ -464,21 +677,6 @@
// only used within a single layer.
uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
- // -----------------------------------------------------------------------
- // Virtuals
-
- // Provide unique string for each class type in the Layer hierarchy
- virtual const char* getType() const = 0;
-
- /*
- * isOpaque - true if this surface is opaque
- *
- * This takes into account the buffer format (i.e. whether or not the
- * pixel format includes an alpha channel) and the "opaque" flag set
- * on the layer. It does not examine the current plane alpha value.
- */
- virtual bool isOpaque(const Layer::State&) const { return false; }
-
/*
* isSecure - true if this surface is secure, that is if it prevents
* screenshots or VNC servers.
@@ -486,11 +684,6 @@
bool isSecure() const;
/*
- * isVisible - true if this layer is visible, false otherwise
- */
- virtual bool isVisible() const = 0;
-
- /*
* isHiddenByPolicy - true if this layer has been forced invisible.
* just because this is false, doesn't mean isVisible() is true.
* For example if this layer has no active buffer, it may not be hidden by
@@ -498,32 +691,6 @@
*/
bool isHiddenByPolicy() const;
- /*
- * Returns whether this layer can receive input.
- */
- virtual bool canReceiveInput() const;
-
- /*
- * isProtected - true if the layer may contain protected content in the
- * GRALLOC_USAGE_PROTECTED sense.
- */
- virtual bool isProtected() const { return false; }
-
- /*
- * isFixedSize - true if content has a fixed size
- */
- virtual bool isFixedSize() const { return true; }
-
- /*
- * usesSourceCrop - true if content should use a source crop
- */
- virtual bool usesSourceCrop() const { return false; }
-
- // Most layers aren't created from the main thread, and therefore need to
- // grab the SF state lock to access HWC, but ContainerLayer does, so we need
- // to avoid grabbing the lock again to avoid deadlock
- virtual bool isCreatedFromMainThread() const { return false; }
-
bool isRemovedFromCurrentState() const;
LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
@@ -538,105 +705,12 @@
void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
- virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; }
- virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; }
- virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; }
- virtual ui::Transform getActiveTransform(const Layer::State& s) const {
- return s.active_legacy.transform;
- }
- virtual Region getActiveTransparentRegion(const Layer::State& s) const {
- return s.activeTransparentRegion_legacy;
- }
- virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
- virtual bool needsFiltering(const DisplayDevice*) const { return false; }
- // True if this layer requires filtering
- // This method is distinct from needsFiltering() in how the filter
- // requirement is computed. needsFiltering() compares displayFrame and crop,
- // where as this method transforms the displayFrame to layer-stack space
- // first. This method should be used if there is no physical display to
- // project onto when taking screenshots, as the filtering requirements are
- // different.
- // If the parent transform needs to be undone when capturing the layer, then
- // the inverse parent transform is also required.
- virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
- return false;
- }
+ InputWindowInfo::Type getWindowType() const { return mWindowType; }
- // This layer is not a clone, but it's the parent to the cloned hierarchy. The
- // variable mClonedChild represents the top layer that will be cloned so this
- // layer will be the parent of mClonedChild.
- // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
- // if the real layer is destroyed, then the clone layer will also be destroyed.
- sp<Layer> mClonedChild;
+ bool getPrimaryDisplayOnly() const;
- virtual sp<Layer> createClone() = 0;
void updateMirrorInfo();
- virtual void updateCloneBufferInfo(){};
-protected:
- sp<compositionengine::LayerFE> asLayerFE() const;
- sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
- bool isClone() { return mClonedFrom != nullptr; }
- bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
-
- virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-
- void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedChildren(const sp<Layer>& mirrorRoot,
- std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void addChildToDrawing(const sp<Layer>&);
- void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
- compositionengine::LayerFE::ClientCompositionTargetSettings&);
- virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
- const LayerFE::LayerSettings&, const Rect& layerStackRect,
- ui::Dataspace outputDataspace);
- // 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;
-
-public:
- /*
- * compositionengine::LayerFE overrides
- */
- const compositionengine::LayerFECompositionState* getCompositionState() const override;
- bool onPreComposition(nsecs_t) override;
- void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
- std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
- compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
- void onLayerDisplayed(const sp<Fence>& releaseFence) override;
- const char* getDebugName() const override;
-
-protected:
- void prepareBasicGeometryCompositionState();
- void prepareGeometryCompositionState();
- virtual void preparePerFrameCompositionState();
- void prepareCursorCompositionState();
-
-public:
- virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
-
- virtual bool isHdrY410() const { return false; }
-
- virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
-
- /*
- * called after composition.
- * returns true if the layer latched a new buffer this frame.
- */
- virtual bool onPostComposition(const DisplayDevice*,
- const std::shared_ptr<FenceTime>& /*glDoneFence*/,
- const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming&) {
- return false;
- }
-
- // If a buffer was replaced this frame, release the former buffer
- virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
-
- virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
- const CompositorTiming& /*compositorTiming*/) {}
/*
* doTransaction - process the transaction. This is a good place to figure
* out which attributes of the surface have changed.
@@ -644,21 +718,6 @@
uint32_t doTransaction(uint32_t transactionFlags);
/*
- * latchBuffer - called each time the screen is redrawn and returns whether
- * the visible regions need to be recomputed (this is a fairly heavy
- * operation, so this should be set only if needed). Typically this is used
- * to figure out if the content or size of a surface has changed.
- */
- virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
- nsecs_t /*expectedPresentTime*/) {
- return false;
- }
-
- virtual bool isBufferLatched() const { return false; }
-
- virtual void latchAndReleaseBuffer() {}
-
- /*
* Remove relative z for the layer if its relative parent is not part of the
* provided layer tree.
*/
@@ -685,36 +744,12 @@
*/
void updateTransformHint(ui::Transform::RotationFlags);
- /*
- * returns the rectangle that crops the content of the layer and scales it
- * to the layer's size.
- */
- virtual Rect getBufferCrop() const { return Rect(); }
-
- /*
- * Returns the transform applied to the buffer.
- */
- virtual uint32_t getBufferTransform() const { return 0; }
-
- virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
-
- virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
-
- /*
- * Returns if a frame is ready
- */
- virtual bool hasReadyFrame() const { return false; }
-
- virtual int32_t getQueuedFrameCount() const { return 0; }
-
- // -----------------------------------------------------------------------
inline const State& getDrawingState() const { return mDrawingState; }
inline const State& getCurrentState() const { return mCurrentState; }
inline State& getCurrentState() { return mCurrentState; }
LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
- static void miniDumpHeader(std::string& result);
void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
void dumpFrameEvents(std::string& result);
@@ -722,17 +757,10 @@
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
-
- virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
- return {};
- }
-
void onDisconnect();
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
FrameEventHistoryDelta* outDelta);
- virtual bool getTransformToDisplayInverse() const { return false; }
-
ui::Transform getTransform() const;
// Returns the Alpha of the Surface, accounting for the Alpha
@@ -749,13 +777,6 @@
// is ready to acquire a buffer.
ui::Transform::RotationFlags getFixedTransformHint() const;
- // Returns how rounded corners should be drawn for this layer.
- // This will traverse the hierarchy until it reaches its root, finding topmost rounded
- // corner definition and converting it into current layer's coordinates.
- // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
- // ignored.
- virtual RoundedCornerState getRoundedCornerState() const;
-
renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const;
/**
@@ -801,22 +822,6 @@
// SurfaceFlinger to complete a transaction.
void commitChildList();
int32_t getZ(LayerVector::StateSet) const;
- virtual void pushPendingState();
-
- /**
- * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
- * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
- */
- virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
-
- /**
- * Returns the source bounds. If the bounds are not defined, it is inferred from the
- * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
- * For the root layer, this is the display viewport size.
- */
- virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
- return parentBounds;
- }
/**
* Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
@@ -827,52 +832,43 @@
Rect getCroppedBufferSize(const Layer::State& s) const;
bool setFrameRate(FrameRate);
- virtual FrameRate getFrameRateForLayerTree() const;
- static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
+
+ virtual void setFrameTimelineVsyncForBuffer(int64_t /*frameTimelineVsyncId*/) {}
+ void setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime);
+
+ // Creates a new handle each time, so we only expect
+ // this to be called once.
+ sp<IBinder> getHandle();
+ const std::string& getName() const { return mName; }
+ bool getPremultipledAlpha() const;
+ void setInputInfo(const InputWindowInfo& info);
+
+ InputWindowInfo fillInputInfo();
+ /**
+ * Returns whether this layer has an explicitly set input-info.
+ */
+ bool hasInputInfo() const;
+
+ uid_t getOwnerUid() { return mOwnerUid; }
+
+ // This layer is not a clone, but it's the parent to the cloned hierarchy. The
+ // variable mClonedChild represents the top layer that will be cloned so this
+ // layer will be the parent of mClonedChild.
+ // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
+ // if the real layer is destroyed, then the clone layer will also be destroyed.
+ sp<Layer> mClonedChild;
+
+ mutable bool contentDirty{false};
+ Region surfaceDamageRegion;
+
+ // 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.
+ int32_t sequence{sSequence++};
+
+ bool mPendingHWCDestroy{false};
protected:
- // constant
- sp<SurfaceFlinger> mFlinger;
- /*
- * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
- * is called.
- */
- class LayerCleaner {
- sp<SurfaceFlinger> mFlinger;
- sp<Layer> mLayer;
-
- protected:
- ~LayerCleaner() {
- // destroy client resources
- mFlinger->onHandleDestroyed(mLayer);
- }
-
- public:
- LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
- : mFlinger(flinger), mLayer(layer) {}
- };
-
- friend class impl::SurfaceInterceptor;
-
- // For unit tests
- friend class TestableSurfaceFlinger;
- friend class RefreshRateSelectionTest;
- friend class SetFrameRateTest;
-
- virtual void commitTransaction(const State& stateToCommit);
-
- uint32_t getEffectiveUsage(uint32_t usage) const;
-
- /**
- * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
- * crop coordinates, transforming them into layer space.
- */
- void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
- void setParent(const sp<Layer>&);
- LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
- void addZOrderRelative(const wp<Layer>& relative);
- void removeZOrderRelative(const wp<Layer>& relative);
-
class SyncPoint {
public:
explicit SyncPoint(uint64_t frameNumber, wp<Layer> requestedSyncLayer)
@@ -900,6 +896,63 @@
wp<Layer> mRequestedSyncLayer;
};
+ friend class impl::SurfaceInterceptor;
+
+ // For unit tests
+ friend class TestableSurfaceFlinger;
+ friend class RefreshRateSelectionTest;
+ friend class SetFrameRateTest;
+
+ virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+ virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+ compositionengine::LayerFE::ClientCompositionTargetSettings&);
+ virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
+ const LayerFE::LayerSettings&, const Rect& layerStackRect,
+ ui::Dataspace outputDataspace);
+ virtual void preparePerFrameCompositionState();
+ virtual void commitTransaction(const State& stateToCommit);
+ virtual bool applyPendingStates(State* stateToCommit);
+ virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
+
+ // Returns mCurrentScaling mode (originating from the
+ // Client) or mOverrideScalingMode mode (originating from
+ // the Surface Controller) if set.
+ virtual uint32_t getEffectiveScalingMode() const { return 0; }
+
+ sp<compositionengine::LayerFE> asLayerFE() const;
+ sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
+ bool isClone() { return mClonedFrom != nullptr; }
+ bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+
+ void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void updateClonedChildren(const sp<Layer>& mirrorRoot,
+ std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+ 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 prepareBasicGeometryCompositionState();
+ void prepareGeometryCompositionState();
+ void prepareCursorCompositionState();
+
+ uint32_t getEffectiveUsage(uint32_t usage) const;
+
+ /**
+ * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
+ * crop coordinates, transforming them into layer space.
+ */
+ void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
+ void setParent(const sp<Layer>&);
+ LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
+ void addZOrderRelative(const wp<Layer>& relative);
+ void removeZOrderRelative(const wp<Layer>& relative);
+ compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+ bool usingRelativeZ(LayerVector::StateSet) const;
+
// SyncPoints which will be signaled when the correct frame is at the head
// of the queue and dropped after the frame has been latched. Protected by
// mLocalSyncPointMutex.
@@ -914,68 +967,14 @@
bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
void popPendingState(State* stateToCommit);
- virtual bool applyPendingStates(State* stateToCommit);
- virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit);
- // Returns mCurrentScaling mode (originating from the
- // Client) or mOverrideScalingMode mode (originating from
- // the Surface Controller) if set.
- virtual uint32_t getEffectiveScalingMode() const { return 0; }
-
-public:
- /*
- * 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), owner(layer) {}
-
- wp<Layer> owner;
- };
-
- // Creates a new handle each time, so we only expect
- // this to be called once.
- sp<IBinder> getHandle();
- const std::string& getName() const { return mName; }
- virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
- virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
- bool getPremultipledAlpha() const;
-
- bool mPendingHWCDestroy{false};
- void setInputInfo(const InputWindowInfo& info);
-
- InputWindowInfo fillInputInfo();
- /**
- * Returns whether this layer has an explicitly set input-info.
- */
- bool hasInputInfo() const;
- /**
- * Return whether this layer needs an input info. For most layer types
- * this is only true if they explicitly set an input-info but BufferLayer
- * overrides this so we can generate input-info for Buffered layers that don't
- * have them (for input occlusion detection checks).
- */
- virtual bool needsInputInfo() const { return hasInputInfo(); }
-
- uid_t getOwnerUid() { return mOwnerUid; }
-
-protected:
- compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
-
- bool usingRelativeZ(LayerVector::StateSet) const;
+ // constant
+ sp<SurfaceFlinger> mFlinger;
bool mPremultipliedAlpha{true};
const std::string mName;
const std::string mTransactionName{"TX - " + mName};
- bool mPrimaryDisplayOnly = false;
-
// These are only accessed by the main thread or the tracing thread.
State mDrawingState;
// Store a copy of the pending state so that the drawing thread can access the
@@ -1038,9 +1037,15 @@
// Window types from WindowManager.LayoutParams
const InputWindowInfo::Type mWindowType;
+ // Can only be accessed with the SF state lock held.
+ std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
+
private:
virtual void setTransformHint(ui::Transform::RotationFlags) {}
+ // Returns true if the layer can draw shadows on its border.
+ virtual bool canDrawShadows() const { return true; }
+
Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
Region getVisibleRegion(const DisplayDevice*) const;
@@ -1059,6 +1064,16 @@
const std::vector<Layer*>& layersInTree);
void updateTreeHasFrameRateVote();
+ void setZOrderRelativeOf(const wp<Layer>& relativeOf);
+ void removeRemoteSyncPoints();
+
+ // Find the root of the cloned hierarchy, this means the first non cloned parent.
+ // This will return null if first non cloned parent is not found.
+ sp<Layer> getClonedRoot();
+
+ // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
+ // null.
+ sp<Layer> getRootLayer();
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
@@ -1075,12 +1090,8 @@
// Layer bounds in screen space.
FloatRect mScreenBounds;
- void setZOrderRelativeOf(const wp<Layer>& relativeOf);
-
bool mGetHandleCalled = false;
- void removeRemoteSyncPoints();
-
// Tracks the process and user id of the caller when creating this layer
// to help debugging.
pid_t mCallingPid;
@@ -1100,17 +1111,6 @@
// final shadow radius for this layer. If a shadow is specified for a layer, then effective
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
float mEffectiveShadowRadius = 0.f;
-
- // Returns true if the layer can draw shadows on its border.
- virtual bool canDrawShadows() const { return true; }
-
- // Find the root of the cloned hierarchy, this means the first non cloned parent.
- // This will return null if first non cloned parent is not found.
- sp<Layer> getClonedRoot();
-
- // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
- // null.
- sp<Layer> getRootLayer();
};
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index d8477e7..f676d5b 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -176,12 +176,13 @@
}
bool RefreshRateOverlay::createLayer() {
+ int32_t layerId;
const status_t ret =
mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
- &mIBinder, &mGbp, nullptr);
+ &mIBinder, &mGbp, nullptr, &layerId);
if (ret) {
ALOGE("failed to create buffer state layer");
return false;
@@ -241,7 +242,8 @@
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
mCurrentFps = refreshRate.getFps();
auto buffer = mBufferCache[*mCurrentFps][mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
@@ -252,7 +254,8 @@
const auto& buffers = mBufferCache[*mCurrentFps];
mFrame = (mFrame + 1) % buffers.size();
auto buffer = buffers[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+ mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 0157a7f..b7b7e46 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -29,6 +29,7 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <cutils/properties.h>
#include <gui/IRegionSamplingListener.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Trace.h>
@@ -38,7 +39,7 @@
#include "DisplayRenderArea.h"
#include "Layer.h"
#include "Promise.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
#include "SurfaceFlinger.h"
namespace android {
@@ -61,7 +62,7 @@
constexpr auto timeForRegionSampling = 5000000ns;
constexpr auto maxRegionSamplingSkips = 10;
-constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingWorkDuration = 3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
// TODO: (b/127403193) duration to string conversion could probably be constexpr
@@ -73,9 +74,9 @@
RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
char value[PROPERTY_VALUE_MAX] = {};
- property_get("debug.sf.region_sampling_offset_ns", value,
- toNsString(defaultRegionSamplingOffset).c_str());
- int const samplingOffsetNsRaw = atoi(value);
+ property_get("debug.sf.region_sampling_duration_ns", value,
+ toNsString(defaultRegionSamplingWorkDuration).c_str());
+ int const samplingDurationNsRaw = atoi(value);
property_get("debug.sf.region_sampling_period_ns", value,
toNsString(defaultRegionSamplingPeriod).c_str());
@@ -87,22 +88,26 @@
if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
- mSamplingOffset = defaultRegionSamplingOffset;
+ mSamplingDuration = defaultRegionSamplingWorkDuration;
mSamplingPeriod = defaultRegionSamplingPeriod;
mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
} else {
- mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+ mSamplingDuration = std::chrono::nanoseconds(samplingDurationNsRaw);
mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
}
}
-struct SamplingOffsetCallback : DispSync::Callback {
+struct SamplingOffsetCallback : VSyncSource::Callback {
SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
- std::chrono::nanoseconds targetSamplingOffset)
+ std::chrono::nanoseconds targetSamplingWorkDuration)
: mRegionSamplingThread(samplingThread),
- mScheduler(scheduler),
- mTargetSamplingOffset(targetSamplingOffset) {}
+ mTargetSamplingWorkDuration(targetSamplingWorkDuration),
+ mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
+ 0ns,
+ /*traceVsync=*/false)) {
+ mVSyncSource->setCallback(this);
+ }
~SamplingOffsetCallback() { stopVsyncListener(); }
@@ -114,8 +119,7 @@
if (mVsyncListening) return;
mPhaseIntervalSetting = Phase::ZERO;
- mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
- mLastCallbackTime);
+ mVSyncSource->setVSyncEnabled(true);
mVsyncListening = true;
}
@@ -128,23 +132,24 @@
void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
if (!mVsyncListening) return;
- mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
+ mVSyncSource->setVSyncEnabled(false);
mVsyncListening = false;
}
- void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final {
+ void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
+ nsecs_t /*deadlineTimestamp*/) final {
std::unique_lock<decltype(mMutex)> lock(mMutex);
if (mPhaseIntervalSetting == Phase::ZERO) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
mPhaseIntervalSetting = Phase::SAMPLING;
- mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
+ mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
return;
}
if (mPhaseIntervalSetting == Phase::SAMPLING) {
mPhaseIntervalSetting = Phase::ZERO;
- mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
+ mVSyncSource->setDuration(0ns, 0ns);
stopVsyncListenerLocked();
lock.unlock();
mRegionSamplingThread.notifySamplingOffset();
@@ -153,16 +158,15 @@
}
RegionSamplingThread& mRegionSamplingThread;
- Scheduler& mScheduler;
- const std::chrono::nanoseconds mTargetSamplingOffset;
+ const std::chrono::nanoseconds mTargetSamplingWorkDuration;
mutable std::mutex mMutex;
- nsecs_t mLastCallbackTime = 0;
enum class Phase {
ZERO,
SAMPLING
} mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
= Phase::ZERO;
bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+ std::unique_ptr<VSyncSource> mVSyncSource;
};
RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
@@ -170,11 +174,12 @@
: mFlinger(flinger),
mScheduler(scheduler),
mTunables(tunables),
- mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
- mTunables.mSamplingTimerTimeout),
- [] {}, [this] { checkForStaleLuma(); }),
+ mIdleTimer(
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ mTunables.mSamplingTimerTimeout),
+ [] {}, [this] { checkForStaleLuma(); }),
mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
- tunables.mSamplingOffset)),
+ tunables.mSamplingDuration)),
lastSampleTime(0ns) {
mThread = std::thread([this]() { threadMain(); });
pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
@@ -183,7 +188,7 @@
RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
: RegionSamplingThread(flinger, scheduler,
- TimingTunables{defaultRegionSamplingOffset,
+ TimingTunables{defaultRegionSamplingWorkDuration,
defaultRegionSamplingPeriod,
defaultRegionSamplingTimerTimeout}) {}
@@ -245,7 +250,7 @@
// until the next vsync deadline, defer this sampling work
// to a later frame, when hopefully there will be more time.
DisplayStatInfo stats;
- mScheduler.getDisplayStatInfo(&stats);
+ mScheduler.getDisplayStatInfo(&stats, systemTime());
if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
mDiscardedFrames++;
@@ -437,14 +442,16 @@
mCachedBuffer->getHeight() == sampledBounds.getHeight()) {
buffer = mCachedBuffer;
} else {
- const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
+ const uint32_t usage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
}
- ScreenCaptureResults captureResults;
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
- true /* regionSampling */, captureResults);
+ true /* regionSampling */, captureListener);
+ ScreenCaptureResults captureResults = captureListener->waitForResults();
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index b9b7a3c..0defdb3 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -43,10 +43,10 @@
class RegionSamplingThread : public IBinder::DeathRecipient {
public:
struct TimingTunables {
- // debug.sf.sampling_offset_ns
- // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
- // at which the sampling should start.
- std::chrono::nanoseconds mSamplingOffset;
+ // debug.sf.sampling_duration_ns
+ // When asynchronously collecting sample, the duration, at which the sampling should start
+ // before a vsync
+ std::chrono::nanoseconds mSamplingDuration;
// debug.sf.sampling_period_ns
// This is the maximum amount of time the luma recieving client
// should have to wait for a new luma value after a frame is updated. The inverse of this is
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
deleted file mode 100644
index 7b2c9c3..0000000
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 <stddef.h>
-
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <ui/FenceTime.h>
-
-#include <memory>
-
-namespace android {
-
-class FenceTime;
-
-class DispSync {
-public:
- class Callback {
- public:
- Callback() = default;
- virtual ~Callback() = default;
- virtual void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
-
- protected:
- Callback(Callback const&) = delete;
- Callback& operator=(Callback const&) = delete;
- };
-
- DispSync() = default;
- virtual ~DispSync() = default;
-
- virtual void reset() = 0;
- virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
- virtual void beginResync() = 0;
- virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) = 0;
- virtual void endResync() = 0;
- virtual void setPeriod(nsecs_t period) = 0;
- virtual nsecs_t getPeriod() = 0;
- virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
- nsecs_t lastCallbackTime) = 0;
- virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
- virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
- virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0;
- virtual void setIgnorePresentFences(bool ignore) = 0;
- virtual nsecs_t expectedPresentTime(nsecs_t now) = 0;
-
- virtual void dump(std::string& result) const = 0;
-
-protected:
- DispSync(DispSync const&) = delete;
- DispSync& operator=(DispSync const&) = delete;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 8752b66..ce5c31a 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -14,10 +14,6 @@
* 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "DispSyncSource.h"
@@ -26,37 +22,129 @@
#include <utils/Trace.h>
#include <mutex>
-#include "DispSync.h"
#include "EventThread.h"
+#include "VsyncController.h"
-namespace android {
+namespace android::scheduler {
using base::StringAppendF;
+using namespace std::chrono_literals;
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+class CallbackRepeater {
+public:
+ CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
+ std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
+ std::chrono::nanoseconds notBefore)
+ : mName(name),
+ mCallback(cb),
+ mRegistration(dispatch,
+ std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3),
+ mName),
+ mStarted(false),
+ mWorkDuration(workDuration),
+ mReadyDuration(readyDuration),
+ mLastCallTime(notBefore) {}
+
+ ~CallbackRepeater() {
+ std::lock_guard lock(mMutex);
+ mRegistration.cancel();
+ }
+
+ void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
+ std::lock_guard lock(mMutex);
+ mStarted = true;
+ mWorkDuration = workDuration;
+ mReadyDuration = readyDuration;
+
+ auto const scheduleResult =
+ mRegistration.schedule({.workDuration = mWorkDuration.count(),
+ .readyDuration = mReadyDuration.count(),
+ .earliestVsync = mLastCallTime.count()});
+ LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
+ "Error scheduling callback: rc %X", scheduleResult);
+ }
+
+ void stop() {
+ std::lock_guard lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
+ mStarted = false;
+ mRegistration.cancel();
+ }
+
+ void dump(std::string& result) const {
+ std::lock_guard lock(mMutex);
+ const auto relativeLastCallTime =
+ mLastCallTime - std::chrono::steady_clock::now().time_since_epoch();
+ StringAppendF(&result, "\t%s: ", mName.c_str());
+ StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
+ mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f);
+ StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f,
+ mStarted ? "running" : "stopped");
+ }
+
+private:
+ void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
+ {
+ std::lock_guard lock(mMutex);
+ mLastCallTime = std::chrono::nanoseconds(vsyncTime);
+ }
+
+ mCallback(vsyncTime, wakeupTime, readyTime);
+
+ {
+ std::lock_guard lock(mMutex);
+ if (!mStarted) {
+ return;
+ }
+ auto const scheduleResult =
+ mRegistration.schedule({.workDuration = mWorkDuration.count(),
+ .readyDuration = mReadyDuration.count(),
+ .earliestVsync = vsyncTime});
+ LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
+ "Error rescheduling callback: rc %X", scheduleResult);
+ }
+ }
+
+ const std::string mName;
+ scheduler::VSyncDispatch::Callback mCallback;
+
+ mutable std::mutex mMutex;
+ VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+ bool mStarted GUARDED_BY(mMutex) = false;
+ std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns;
+ std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns;
+ std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns;
+};
+
+DispSyncSource::DispSyncSource(scheduler::VSyncDispatch& vSyncDispatch,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration, bool traceVsync,
const char* name)
: mName(name),
mValue(base::StringPrintf("VSYNC-%s", name), 0),
mTraceVsync(traceVsync),
mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
- mDispSync(dispSync),
- mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
+ mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
+ mReadyDuration(readyDuration) {
+ mCallbackRepeater =
+ std::make_unique<CallbackRepeater>(vSyncDispatch,
+ std::bind(&DispSyncSource::onVsyncCallback, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ name, workDuration, readyDuration,
+ std::chrono::steady_clock::now().time_since_epoch());
+}
+
+DispSyncSource::~DispSyncSource() = default;
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
if (enable) {
- status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
- static_cast<DispSync::Callback*>(this),
- mLastCallbackTime);
- if (err != NO_ERROR) {
- ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
- }
+ mCallbackRepeater->start(mWorkDuration, mReadyDuration);
// ATRACE_INT(mVsyncOnLabel.c_str(), 1);
} else {
- status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this),
- &mLastCallbackTime);
- if (err != NO_ERROR) {
- ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err);
- }
+ mCallbackRepeater->stop();
// ATRACE_INT(mVsyncOnLabel.c_str(), 0);
}
mEnabled = enable;
@@ -67,32 +155,22 @@
mCallback = callback;
}
-void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
+void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
std::lock_guard lock(mVsyncMutex);
- const nsecs_t period = mDispSync->getPeriod();
-
- // Normalize phaseOffset to [-period, period)
- const int numPeriods = phaseOffset / period;
- phaseOffset -= numPeriods * period;
- if (mPhaseOffset == phaseOffset) {
- return;
- }
-
- mPhaseOffset = phaseOffset;
+ mWorkDuration = workDuration;
+ mReadyDuration = readyDuration;
// If we're not enabled, we don't need to mess with the listeners
if (!mEnabled) {
return;
}
- status_t err =
- mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset);
- if (err != NO_ERROR) {
- ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
- }
+ mCallbackRepeater->start(mWorkDuration, mReadyDuration);
}
-void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
+void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
+ nsecs_t readyTime) {
VSyncSource::Callback* callback;
{
std::lock_guard lock(mCallbackMutex);
@@ -104,17 +182,13 @@
}
if (callback != nullptr) {
- callback->onVSyncEvent(when, expectedVSyncTimestamp);
+ callback->onVSyncEvent(targetWakeupTime, vsyncTime, readyTime);
}
}
void DispSyncSource::dump(std::string& result) const {
std::lock_guard lock(mVsyncMutex);
StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
- mDispSync->dump(result);
}
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 2aee3f6..2fce235 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -18,45 +18,47 @@
#include <mutex>
#include <string>
-#include "DispSync.h"
#include "EventThread.h"
#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
-namespace android {
+namespace android::scheduler {
+class CallbackRepeater;
-class DispSyncSource final : public VSyncSource, private DispSync::Callback {
+class DispSyncSource final : public VSyncSource {
public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+ DispSyncSource(VSyncDispatch& vSyncDispatch, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name);
- ~DispSyncSource() override = default;
+ ~DispSyncSource() override;
// The following methods are implementation of VSyncSource.
const char* getName() const override { return mName; }
void setVSyncEnabled(bool enable) override;
void setCallback(VSyncSource::Callback* callback) override;
- void setPhaseOffset(nsecs_t phaseOffset) override;
+ void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) override;
void dump(std::string&) const override;
private:
- // The following method is the implementation of the DispSync::Callback.
- void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
+ void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
const char* const mName;
TracedOrdinal<int> mValue;
const bool mTraceVsync;
const std::string mVsyncOnLabel;
- nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
- DispSync* mDispSync;
+ std::unique_ptr<CallbackRepeater> mCallbackRepeater;
std::mutex mCallbackMutex;
VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
mutable std::mutex mVsyncMutex;
- TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
+ TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex);
+ std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex);
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 846190c..bf2a509 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -40,6 +40,7 @@
#include <utils/Trace.h>
#include "EventThread.h"
+#include "FrameTimeline.h"
#include "HwcStrongTypes.h"
using namespace std::chrono_literals;
@@ -61,6 +62,8 @@
return "VSyncRequest::None";
case VSyncRequest::Single:
return "VSyncRequest::Single";
+ case VSyncRequest::SingleSuppressCallback:
+ return "VSyncRequest::SingleSuppressCallback";
default:
return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request));
}
@@ -98,11 +101,14 @@
}
DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
- uint32_t count, nsecs_t expectedVSyncTimestamp) {
+ uint32_t count, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp, int64_t vsyncId) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
event.vsync.count = count;
event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
+ event.vsync.deadlineTimestamp = deadlineTimestamp;
+ event.vsync.vsyncId = vsyncId;
return event;
}
@@ -163,8 +169,10 @@
namespace impl {
EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+ android::frametimeline::TokenManager* tokenManager,
InterceptVSyncsCallback interceptVSyncsCallback)
: mVSyncSource(std::move(vsyncSource)),
+ mTokenManager(tokenManager),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
mThreadName(mVSyncSource->getName()) {
mVSyncSource->setCallback(this);
@@ -200,9 +208,10 @@
mThread.join();
}
-void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+void EventThread::setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
std::lock_guard<std::mutex> lock(mMutex);
- mVSyncSource->setPhaseOffset(phaseOffset);
+ mVSyncSource->setDuration(workDuration, readyDuration);
}
sp<EventThreadConnection> EventThread::createEventConnection(
@@ -260,6 +269,8 @@
if (connection->vsyncRequest == VSyncRequest::None) {
connection->vsyncRequest = VSyncRequest::Single;
mCondition.notify_all();
+ } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {
+ connection->vsyncRequest = VSyncRequest::Single;
}
}
@@ -283,12 +294,21 @@
mCondition.notify_all();
}
-void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) {
+void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
std::lock_guard<std::mutex> lock(mMutex);
LOG_FATAL_IF(!mVSyncState);
+ const int64_t vsyncId = [&] {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+ }
+ return static_cast<int64_t>(0);
+ }();
+
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
- expectedVSyncTimestamp));
+ expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
mCondition.notify_all();
}
@@ -410,9 +430,12 @@
LOG_FATAL_IF(!mVSyncState);
const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
- const auto expectedVSyncTime = now + timeout.count();
+ const auto deadlineTimestamp = now + timeout.count();
+ const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
+ // TODO(b/162890590): use TokenManager to populate vsyncId
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
- ++mVSyncState->count, expectedVSyncTime));
+ ++mVSyncState->count, expectedVSyncTime,
+ deadlineTimestamp, /*vsyncId=*/0));
}
}
}
@@ -432,8 +455,11 @@
switch (connection->vsyncRequest) {
case VSyncRequest::None:
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
+ return false;
+ case VSyncRequest::Single:
+ connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
case VSyncRequest::Periodic:
return true;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 49f624c..e42ca05 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -41,13 +41,20 @@
class EventThreadTest;
class SurfaceFlinger;
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
// ---------------------------------------------------------------------------
using ResyncCallback = std::function<void()>;
enum class VSyncRequest {
- None = -1,
- Single = 0,
+ None = -2,
+ // Single wakes up for the next two frames to avoid scheduler overhead
+ Single = -1,
+ // SingleSuppressCallback only wakes up for the next frame
+ SingleSuppressCallback = 0,
Periodic = 1,
// Subsequent values are periods.
};
@@ -57,7 +64,8 @@
class Callback {
public:
virtual ~Callback() {}
- virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
+ virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) = 0;
};
virtual ~VSyncSource() {}
@@ -65,7 +73,8 @@
virtual const char* getName() const = 0;
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
- virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) = 0;
virtual void dump(std::string& result) const = 0;
};
@@ -116,7 +125,8 @@
virtual void dump(std::string& result) const = 0;
- virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) = 0;
virtual status_t registerDisplayEventConnection(
const sp<EventThreadConnection>& connection) = 0;
@@ -134,7 +144,8 @@
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
- EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
+ EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*,
+ InterceptVSyncsCallback);
~EventThread();
sp<EventThreadConnection> createEventConnection(
@@ -157,7 +168,8 @@
void dump(std::string& result) const override;
- void setPhaseOffset(nsecs_t phaseOffset) override;
+ void setDuration(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) override;
size_t getEventThreadConnectionCount() override;
@@ -177,9 +189,11 @@
REQUIRES(mMutex);
// Implements VSyncSource::Callback
- void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) override;
+ void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) override;
const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
+ frametimeline::TokenManager* const mTokenManager;
const InterceptVSyncsCallback mInterceptVSyncsCallback;
const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 975c9db..016b076 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -35,16 +35,17 @@
mCallback = callback;
}
- void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
+ void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
std::lock_guard<std::mutex> lock(mCallbackMutex);
if (mCallback) {
- mCallback->onVSyncEvent(when, expectedVSyncTimestamp);
+ mCallback->onVSyncEvent(when, expectedVSyncTimestamp, deadlineTimestamp);
}
}
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
- void setPhaseOffset(nsecs_t) override {}
+ void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {}
void dump(std::string&) const override {}
private:
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 6067e69..1343375 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,9 +14,7 @@
* 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <binder/IPCThreadState.h>
@@ -28,6 +26,7 @@
#include <gui/IDisplayEventConnection.h>
#include "EventThread.h"
+#include "FrameTimeline.h"
#include "MessageQueue.h"
#include "SurfaceFlinger.h"
@@ -39,8 +38,9 @@
}
}
-void MessageQueue::Handler::dispatchInvalidate(nsecs_t expectedVSyncTimestamp) {
+void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ mVsyncId = vsyncId;
mExpectedVSyncTime = expectedVSyncTimestamp;
mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
}
@@ -50,11 +50,11 @@
switch (message.what) {
case INVALIDATE:
android_atomic_and(~eventMaskInvalidate, &mEventMask);
- mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+ mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
case REFRESH:
android_atomic_and(~eventMaskRefresh, &mEventMask);
- mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+ mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
break;
}
}
@@ -67,15 +67,53 @@
mHandler = new Handler(*this);
}
+// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
+// and remove the EventThread from MessageQueue
void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
if (mEventTube.getFd() >= 0) {
mLooper->removeFd(mEventTube.getFd());
}
mEvents = connection;
- mEvents->stealReceiveChannel(&mEventTube);
- mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
- this);
+ if (mEvents) {
+ mEvents->stealReceiveChannel(&mEventTube);
+ mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+ this);
+ }
+}
+
+void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+ ATRACE_CALL();
+ // Trace VSYNC-sf
+ mVsync.value = (mVsync.value + 1) % 2;
+
+ {
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+ }
+ mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
+ {targetWakeupTime, readyTime, vsyncTime}),
+ vsyncTime);
+}
+
+void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+ frametimeline::TokenManager& tokenManager,
+ std::chrono::nanoseconds workDuration) {
+ setDuration(workDuration);
+ mVsync.tokenManager = &tokenManager;
+ mVsync.registration = std::make_unique<
+ scheduler::VSyncCallbackRegistration>(dispatch,
+ std::bind(&MessageQueue::vsyncCallback, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ "sf");
+}
+
+void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
+ ATRACE_CALL();
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.workDuration = workDuration;
}
void MessageQueue::waitMessage() {
@@ -105,7 +143,18 @@
}
void MessageQueue::invalidate() {
- mEvents->requestNextVsync();
+ ATRACE_CALL();
+ if (mEvents) {
+ mEvents->requestNextVsync();
+ } else {
+ const auto [workDuration, lastVsyncCallback] = [&] {
+ std::lock_guard lock(mVsync.mutex);
+ std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration;
+ return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count());
+ }();
+
+ mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback});
+ }
}
void MessageQueue::refresh() {
@@ -123,7 +172,8 @@
while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
for (int i = 0; i < n; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- mHandler->dispatchInvalidate(buffer[i].vsync.expectedVSyncTimestamp);
+ mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
+ buffer[i].vsync.expectedVSyncTimestamp);
break;
}
}
@@ -133,5 +183,3 @@
} // namespace android::impl
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 132b416..139b38e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,6 +29,8 @@
#include <private/gui/BitTube.h>
#include "EventThread.h"
+#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
namespace android {
@@ -63,6 +65,9 @@
virtual ~MessageQueue() = default;
virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+ virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
@@ -74,18 +79,20 @@
namespace impl {
-class MessageQueue final : public android::MessageQueue {
+class MessageQueue : public android::MessageQueue {
+protected:
class Handler : public MessageHandler {
enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
MessageQueue& mQueue;
int32_t mEventMask;
+ std::atomic<int64_t> mVsyncId;
std::atomic<nsecs_t> mExpectedVSyncTime;
public:
explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
- virtual void handleMessage(const Message& message);
- void dispatchRefresh();
- void dispatchInvalidate(nsecs_t expectedVSyncTimestamp);
+ void handleMessage(const Message& message) override;
+ virtual void dispatchRefresh();
+ virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
};
friend class Handler;
@@ -93,15 +100,33 @@
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
sp<EventThreadConnection> mEvents;
+
+ struct Vsync {
+ frametimeline::TokenManager* tokenManager = nullptr;
+ std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
+
+ std::mutex mutex;
+ TracedOrdinal<std::chrono::nanoseconds> workDuration
+ GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
+ std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+ TracedOrdinal<int> value = {"VSYNC-sf", 0};
+ };
+
+ Vsync mVsync;
+
gui::BitTube mEventTube;
sp<Handler> mHandler;
static int cb_eventReceiver(int fd, int events, void* data);
int eventReceiver(int fd, int events);
+ void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
public:
~MessageQueue() override = default;
void init(const sp<SurfaceFlinger>& flinger) override;
+ void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) override;
+ void setDuration(std::chrono::nanoseconds workDuration) override;
void setEventConnection(const sp<EventThreadConnection>& connection) override;
void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5f7b2c2..7b8448f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -31,6 +31,7 @@
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <FrameTimeline/FrameTimeline.h>
#include <algorithm>
#include <cinttypes>
#include <cstdint>
@@ -39,7 +40,6 @@
#include <numeric>
#include "../Layer.h"
-#include "DispSync.h"
#include "DispSyncSource.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
@@ -50,6 +50,7 @@
#include "VSyncDispatchTimerQueue.h"
#include "VSyncPredictor.h"
#include "VSyncReactor.h"
+#include "VsyncController.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -95,6 +96,26 @@
} // namespace
+class PredictedVsyncTracer {
+public:
+ PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch)
+ : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this),
+ "PredictedVsyncTracer") {
+ scheduleRegistration();
+ }
+
+private:
+ TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+ scheduler::VSyncCallbackRegistration mRegistration;
+
+ void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); }
+
+ void callback() {
+ mParity = !mParity;
+ scheduleRegistration();
+ }
+};
+
Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
: Scheduler(configs, callback,
{.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
@@ -104,7 +125,7 @@
Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
Options options)
- : Scheduler(createVsyncSchedule(options), configs, callback,
+ : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
createLayerHistory(configs, options.useContentDetectionV2), options) {
using namespace sysprop;
@@ -145,7 +166,11 @@
mVsyncSchedule(std::move(schedule)),
mLayerHistory(std::move(layerHistory)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(configs) {
+ mRefreshRateConfigs(configs),
+ mPredictedVsyncTracer(
+ base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
+ ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
+ : nullptr) {
mSchedulerCallback.setVsyncEnabled(false);
}
@@ -156,17 +181,17 @@
mIdleTimer.reset();
}
-Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(Options options) {
+Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
auto clock = std::make_unique<scheduler::SystemClock>();
auto tracker = createVSyncTracker();
auto dispatch = createVSyncDispatch(*tracker);
// TODO(b/144707443): Tune constants.
constexpr size_t pendingFenceLimit = 20;
- auto sync = std::make_unique<scheduler::VSyncReactor>(std::move(clock), *dispatch, *tracker,
- pendingFenceLimit,
- options.supportKernelTimer);
- return {std::move(sync), std::move(tracker), std::move(dispatch)};
+ auto controller =
+ std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
+ supportKernelTimer);
+ return {std::move(controller), std::move(tracker), std::move(dispatch)};
}
std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
@@ -180,21 +205,19 @@
return std::make_unique<scheduler::impl::LayerHistory>();
}
-DispSync& Scheduler::getPrimaryDispSync() {
- return *mVsyncSchedule.sync;
-}
-
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
- nsecs_t phaseOffsetNs) {
- return std::make_unique<DispSyncSource>(&getPrimaryDispSync(), phaseOffsetNs,
- true /* traceVsync */, name);
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
+ const char* name, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration, bool traceVsync) {
+ return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration,
+ readyDuration, traceVsync, name);
}
Scheduler::ConnectionHandle Scheduler::createConnection(
- const char* connectionName, nsecs_t phaseOffsetNs,
+ const char* connectionName, frametimeline::TokenManager* tokenManager,
+ std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
- auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
+ auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
+ auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
std::move(interceptCallback));
return createConnection(std::move(eventThread));
}
@@ -206,6 +229,7 @@
auto connection =
createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
return handle;
}
@@ -217,29 +241,47 @@
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
}
sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
return mConnections[handle].connection;
}
void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
bool connected) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onHotplugReceived(displayId, connected);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+
+ thread->onHotplugReceived(displayId, connected);
}
void Scheduler::onScreenAcquired(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onScreenAcquired();
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onScreenAcquired();
}
void Scheduler::onScreenReleased(ConnectionHandle handle) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onScreenReleased();
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onScreenReleased();
}
void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
@@ -251,6 +293,16 @@
}
void Scheduler::dispatchCachedReportedConfig() {
+ // Check optional fields first.
+ if (!mFeatures.configId.has_value()) {
+ ALOGW("No config ID found, not dispatching cached config.");
+ return;
+ }
+ if (!mFeatures.cachedConfigChangedParams.has_value()) {
+ ALOGW("No config changed params found, not dispatching cached config.");
+ return;
+ }
+
const auto configId = *mFeatures.configId;
const auto vsyncPeriod =
mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
@@ -272,28 +324,45 @@
void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
PhysicalDisplayId displayId,
HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->onConfigChanged(displayId, configId, vsyncPeriod);
}
size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, 0);
return mConnections[handle].thread->getEventThreadConnectionCount();
}
void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections.at(handle).thread->dump(result);
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections.at(handle).thread.get();
+ }
+ thread->dump(result);
}
-void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
- RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->setPhaseOffset(phaseOffset);
+void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
+ android::EventThread* thread;
+ {
+ std::lock_guard<std::mutex> lock(mConnectionsLock);
+ RETURN_IF_INVALID_HANDLE(handle);
+ thread = mConnections[handle].thread.get();
+ }
+ thread->setDuration(workDuration, readyDuration);
}
-void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
- stats->vsyncTime = getPrimaryDispSync().computeNextRefresh(0, systemTime());
- stats->vsyncPeriod = getPrimaryDispSync().getPeriod();
+void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now) {
+ stats->vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
+ stats->vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
}
Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -309,6 +378,7 @@
auto eventThread =
std::make_unique<impl::EventThread>(std::move(vsyncSource),
+ /*tokenManager=*/nullptr,
impl::EventThread::InterceptVSyncsCallback());
mInjectorConnectionHandle = createConnection(std::move(eventThread));
@@ -318,19 +388,19 @@
return mInjectorConnectionHandle;
}
-bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime) {
+bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) {
if (!mInjectVSyncs || !mVSyncInjector) {
return false;
}
- mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime);
+ mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp);
return true;
}
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- getPrimaryDispSync().beginResync();
+ mVsyncSchedule.tracker->resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
@@ -340,7 +410,6 @@
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
mSchedulerCallback.setVsyncEnabled(false);
- getPrimaryDispSync().endResync();
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
@@ -380,10 +449,10 @@
void Scheduler::setVsyncPeriod(nsecs_t period) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
- getPrimaryDispSync().setPeriod(period);
+ mVsyncSchedule.controller->startPeriodTransition(period);
if (!mPrimaryHWVsyncEnabled) {
- getPrimaryDispSync().beginResync();
+ mVsyncSchedule.tracker->resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
@@ -396,8 +465,8 @@
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync =
- getPrimaryDispSync().addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
+ needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+ periodFlushed);
}
}
@@ -409,7 +478,7 @@
}
void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
- if (getPrimaryDispSync().addPresentFence(fenceTime)) {
+ if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
@@ -417,11 +486,7 @@
}
void Scheduler::setIgnorePresentFences(bool ignore) {
- getPrimaryDispSync().setIgnorePresentFences(ignore);
-}
-
-nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
- return getPrimaryDispSync().expectedPresentTime(now);
+ mVsyncSchedule.controller->setIgnorePresentFences(ignore);
}
void Scheduler::registerLayer(Layer* layer) {
@@ -555,7 +620,7 @@
refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
- // need to update the DispSync model anyway.
+ // need to update the VsyncController model anyway.
disableHardwareVsync(false /* makeUnavailable */);
}
@@ -598,6 +663,15 @@
mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
}
+void Scheduler::dumpVsync(std::string& s) const {
+ using base::StringAppendF;
+
+ StringAppendF(&s, "VSyncReactor:\n");
+ mVsyncSchedule.controller->dump(s);
+ StringAppendF(&s, "VSyncDispatch:\n");
+ mVsyncSchedule.dispatch->dump(s);
+}
+
template <class T>
bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
HwcConfigIndexType newConfigId;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ed68b86..47ce4a4 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -40,15 +40,20 @@
using namespace std::chrono_literals;
using scheduler::LayerHistory;
-class DispSync;
class FenceTime;
class InjectVSyncSource;
+class PredictedVsyncTracer;
namespace scheduler {
+class VsyncController;
class VSyncDispatch;
class VSyncTracker;
} // namespace scheduler
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
struct ISchedulerCallback {
virtual void setVsyncEnabled(bool) = 0;
virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
@@ -68,10 +73,10 @@
Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
~Scheduler();
- DispSync& getPrimaryDispSync();
-
using ConnectionHandle = scheduler::ConnectionHandle;
- ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+ ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -88,17 +93,16 @@
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
- // Modifies phase offset in the event thread.
- void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
+ // Modifies work duration in the event thread.
+ void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration);
- void getDisplayStatInfo(DisplayStatInfo* stats);
+ void getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now);
// 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);
-
+ bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp);
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
@@ -110,13 +114,12 @@
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
void resync();
- // Passes a vsync sample to DispSync. periodFlushed will be true if
- // DispSync detected that the vsync period changed, and false otherwise.
+ // Passes a vsync sample to VsyncController. periodFlushed will be true if
+ // VsyncController detected that the vsync period changed, and false otherwise.
void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed);
void addPresentFence(const std::shared_ptr<FenceTime>&);
void setIgnorePresentFences(bool ignore);
- nsecs_t getDispSyncExpectedPresentTime(nsecs_t now);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
@@ -134,8 +137,11 @@
void setDisplayPowerState(bool normal);
+ scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
+ void dumpVsync(std::string&) const;
// Get the appropriate refresh for current conditions.
std::optional<HwcConfigIndexType> getPreferredConfigId();
@@ -151,6 +157,11 @@
size_t getEventThreadConnectionCount(ConnectionHandle handle);
+ std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration,
+ bool traceVsync = true);
+
private:
friend class TestableScheduler;
@@ -170,7 +181,7 @@
};
struct VsyncSchedule {
- std::unique_ptr<DispSync> sync;
+ std::unique_ptr<scheduler::VsyncController> controller;
std::unique_ptr<scheduler::VSyncTracker> tracker;
std::unique_ptr<scheduler::VSyncDispatch> dispatch;
};
@@ -182,12 +193,10 @@
Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
std::unique_ptr<LayerHistory>, Options);
- static VsyncSchedule createVsyncSchedule(Options);
+ static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
bool useContentDetectionV2);
- std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
-
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
sp<EventThreadConnection> createConnectionInternal(EventThread*,
@@ -221,7 +230,8 @@
};
ConnectionHandle::Id mNextConnectionHandleId = 0;
- std::unordered_map<ConnectionHandle, Connection> mConnections;
+ mutable std::mutex mConnectionsLock;
+ std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
bool mInjectVSyncs = false;
InjectVSyncSource* mVSyncInjector = nullptr;
@@ -280,6 +290,8 @@
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+
+ const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
index da2195c..40dd841 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -43,10 +43,10 @@
virtual ~TimeKeeper();
/*
- * Arms callback to fired in time nanoseconds.
+ * Arms callback to fired when time is current based on CLOCK_MONOTONIC
* There is only one timer, and subsequent calls will reset the callback function and the time.
*/
- virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+ virtual void alarmAt(std::function<void()> const& callback, nsecs_t time) = 0;
/*
* Cancels an existing pending callback
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 7c5058e..c9c2d84 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -88,8 +88,8 @@
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
+ std::lock_guard lock(mMutex);
using namespace std::literals;
static constexpr int ns_per_s =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
@@ -99,17 +99,17 @@
struct itimerspec old_timer;
struct itimerspec new_timer {
.it_interval = {.tv_sec = 0, .tv_nsec = 0},
- .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
- .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+ .it_value = {.tv_sec = static_cast<long>(time / ns_per_s),
+ .tv_nsec = static_cast<long>(time % ns_per_s)},
};
- if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+ if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
void Timer::alarmCancel() {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
struct itimerspec old_timer;
struct itimerspec new_timer {
@@ -192,7 +192,7 @@
setDebugState(DebugState::Running);
std::function<void()> cb;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
cb = mCallback;
}
if (cb) {
@@ -211,7 +211,7 @@
}
void Timer::setDebugState(DebugState state) {
- std::lock_guard lk(mMutex);
+ std::lock_guard lock(mMutex);
mDebugState = state;
}
@@ -233,7 +233,7 @@
}
void Timer::dump(std::string& result) const {
- std::lock_guard lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
}
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
index a8e2d5a..69ce079 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -30,9 +30,9 @@
~Timer();
nsecs_t now() const final;
- // NB: alarmIn and alarmCancel are threadsafe; with the last-returning function being effectual
+ // NB: alarmAt and alarmCancel are threadsafe; with the last-returning function being effectual
// Most users will want to serialize thes calls so as to be aware of the timer state.
- void alarmIn(std::function<void()> const& cb, nsecs_t fireIn) final;
+ void alarmAt(std::function<void()> const& cb, nsecs_t time) final;
void alarmCancel() final;
void dump(std::string& result) const final;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 2a2d7c5..9d71103 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -40,11 +40,13 @@
/*
* A callback that can be registered to be awoken at a given time relative to a vsync event.
- * \param [in] vsyncTime The timestamp of the vsync the callback is for.
- * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb.
- *
+ * \param [in] vsyncTime: The timestamp of the vsync the callback is for.
+ * \param [in] targetWakeupTime: The timestamp of intended wakeup time of the cb.
+ * \param [in] readyTime: The timestamp of intended time where client needs to finish
+ * its work by.
*/
- using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>;
+ using Callback =
+ std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime)>;
/*
* Registers a callback that will be called at designated points on the vsync timeline.
@@ -71,33 +73,61 @@
virtual void unregisterCallback(CallbackToken token) = 0;
/*
+ * Timing information about a scheduled callback
+ *
+ * @workDuration: The time needed for the client to perform its work
+ * @readyDuration: The time needed for the client to be ready before a vsync event.
+ * For external (non-SF) clients, not only do we need to account for their
+ * workDuration, but we also need to account for the time SF will take to
+ * process their buffer/transaction. In this case, readyDuration will be set
+ * to the SF duration in order to provide enough end-to-end time, and to be
+ * able to provide the ready-by time (deadline) on the callback.
+ * For internal clients, we don't need to add additional padding, so
+ * readyDuration will typically be 0.
+ * @earliestVsync: The targeted display time. This will be snapped to the closest
+ * predicted vsync time after earliestVsync.
+ *
+ * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
+ * event.
+ */
+ struct ScheduleTiming {
+ nsecs_t workDuration = 0;
+ nsecs_t readyDuration = 0;
+ nsecs_t earliestVsync = 0;
+
+ bool operator==(const ScheduleTiming& other) const {
+ return workDuration == other.workDuration && readyDuration == other.readyDuration &&
+ earliestVsync == other.earliestVsync;
+ }
+
+ bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
+ };
+
+ /*
* Schedules the registered callback to be dispatched.
*
- * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+ * The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
+ * event.
*
* The caller designates the earliest vsync event that should be targeted by the earliestVsync
* parameter.
- * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
- * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+ * The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where
+ * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ).
*
- * If (workDuration - earliestVsync) is in the past, or if a callback has already been
- * dispatched for the predictedVsync, an error will be returned.
+ * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has
+ * already been dispatched for the predictedVsync, an error will be returned.
*
* It is valid to reschedule a callback to a different time.
*
* \param [in] token The callback to schedule.
- * \param [in] workDuration The time before the actual vsync time to invoke the callback
- * associated with token.
- * \param [in] earliestVsync The targeted display time. This will be snapped to the closest
- * predicted vsync time after earliestVsync.
+ * \param [in] scheduleTiming The timing information for this schedule call
* \return A ScheduleResult::Scheduled if callback was scheduled.
* A ScheduleResult::CannotSchedule
- * if (workDuration - earliestVsync) is in the past, or
- * if a callback was dispatched for the predictedVsync already.
- * A ScheduleResult::Error if there was another error.
+ * if (workDuration + readyDuration - earliestVsync) is in the past,
+ * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
+ * there was another error.
*/
- virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) = 0;
+ virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
/* Cancels a scheduled callback, if possible.
*
@@ -129,7 +159,7 @@
~VSyncCallbackRegistration();
// See documentation for VSyncDispatch::schedule.
- ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+ ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
// See documentation for VSyncDispatch::cancel.
CancelResult cancel();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 2a6fd05..ca6ea27 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -35,8 +35,6 @@
nsecs_t minVsyncDistance)
: mName(name),
mCallback(cb),
- mWorkDuration(0),
- mEarliestVsync(0),
mMinVsyncDistance(minVsyncDistance) {}
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
@@ -54,6 +52,13 @@
return {mArmedInfo->mActualWakeupTime};
}
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const {
+ if (!mArmedInfo) {
+ return {};
+ }
+ return {mArmedInfo->mActualReadyTime};
+}
+
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
if (!mArmedInfo) {
return {};
@@ -61,10 +66,10 @@
return {mArmedInfo->mActualVsyncTime};
}
-ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
- auto nextVsyncTime =
- tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+ auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+ std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
bool const wouldSkipAVsyncTarget =
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
@@ -80,16 +85,15 @@
tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
}
- auto const nextWakeupTime = nextVsyncTime - workDuration;
- mWorkDuration = workDuration;
- mEarliestVsync = earliestVsync;
- mArmedInfo = {nextWakeupTime, nextVsyncTime};
+ auto const nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
+ auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
+ mScheduleTiming = timing;
+ mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
return ScheduleResult::Scheduled;
}
-void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration,
- nsecs_t earliestVsync) {
- mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration};
+void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
+ mWorkloadUpdateInfo = timing;
}
bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
@@ -102,14 +106,18 @@
}
if (mWorkloadUpdateInfo) {
- mEarliestVsync = mWorkloadUpdateInfo->earliestVsync;
- mWorkDuration = mWorkloadUpdateInfo->duration;
+ mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
- auto const nextVsyncTime =
- tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
- mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+ const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
+ const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
+
+ const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync);
+ const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
+ const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
+
+ mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
}
void VSyncDispatchTimerQueueEntry::disarm() {
@@ -122,13 +130,14 @@
return *mLastDispatchTime;
}
-void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) {
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp,
+ nsecs_t deadlineTimestamp) {
{
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = true;
}
- mCallback(vsyncTimestamp, wakeupTimestamp);
+ mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp);
std::lock_guard<std::mutex> lk(mRunningMutex);
mRunning = false;
@@ -144,15 +153,20 @@
std::lock_guard<std::mutex> lk(mRunningMutex);
std::string armedInfo;
if (mArmedInfo) {
- StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+ StringAppendF(&armedInfo,
+ "[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]",
(mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+ (mArmedInfo->mActualReadyTime - systemTime()) / 1e6f,
(mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
}
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
- StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
- mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+ StringAppendF(&result,
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative "
+ "to now\n",
+ mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
+ (mScheduleTiming.earliestVsync - systemTime()) / 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
@@ -171,7 +185,7 @@
mMinVsyncDistance(minVsyncDistance) {}
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
cancelTimer();
}
@@ -180,10 +194,10 @@
mTimeKeeper->alarmCancel();
}
-void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
mIntendedWakeupTime = targetTime;
- mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
- targetTime - now);
+ mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+ mIntendedWakeupTime);
mLastTimerSchedule = mTimeKeeper->now();
}
@@ -239,10 +253,11 @@
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t vsyncTimestamp;
nsecs_t wakeupTimestamp;
+ nsecs_t deadlineTimestamp;
};
std::vector<Invocation> invocations;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto const now = mTimeKeeper->now();
mLastTimerCallback = now;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
@@ -252,11 +267,13 @@
continue;
}
+ auto const readyTime = callback->readyTime();
+
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
- invocations.emplace_back(
- Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
+ invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
+ *wakeupTime, *readyTime});
}
}
@@ -265,13 +282,14 @@
}
for (auto const& invocation : invocations) {
- invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
+ invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
+ invocation.deadlineTimestamp);
}
}
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
Callback const& callbackFn, std::string callbackName) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
return CallbackToken{
mCallbacks
.emplace(++mCallbackToken,
@@ -284,7 +302,7 @@
void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it != mCallbacks.end()) {
entry = it->second;
@@ -297,11 +315,11 @@
}
}
-ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
- nsecs_t earliestVsync) {
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
+ ScheduleTiming scheduleTiming) {
auto result = ScheduleResult::Error;
{
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
@@ -314,11 +332,11 @@
* timer recalculation to avoid cancelling a callback that is about to fire. */
auto const rearmImminent = now > mIntendedWakeupTime;
if (CC_UNLIKELY(rearmImminent)) {
- callback->addPendingWorkloadUpdate(workDuration, earliestVsync);
+ callback->addPendingWorkloadUpdate(scheduleTiming);
return ScheduleResult::Scheduled;
}
- result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+ result = callback->schedule(scheduleTiming, mTracker, now);
if (result == ScheduleResult::CannotSchedule) {
return result;
}
@@ -332,7 +350,7 @@
}
CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto it = mCallbacks.find(token);
if (it == mCallbacks.end()) {
@@ -354,7 +372,7 @@
}
void VSyncDispatchTimerQueue::dump(std::string& result) const {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "\tTimer:\n");
mTimeKeeper->dump(result);
StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
@@ -396,11 +414,11 @@
if (mValidToken) mDispatch.get().unregisterCallback(mToken);
}
-ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
if (!mValidToken) {
return ScheduleResult::Error;
}
- return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+ return mDispatch.get().schedule(mToken, scheduleTiming);
}
CancelResult VSyncCallbackRegistration::cancel() {
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 957c0d1..26237b6 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -47,7 +47,7 @@
std::optional<nsecs_t> lastExecutedVsyncTarget() const;
// This moves the state from disarmed->armed and will calculate the wakeupTime.
- ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+ ScheduleResult schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker,
nsecs_t now);
// This will update armed entries with the latest vsync information. Entry remains armed.
void update(VSyncTracker& tracker, nsecs_t now);
@@ -56,6 +56,8 @@
// It will not update the wakeupTime.
std::optional<nsecs_t> wakeupTime() const;
+ std::optional<nsecs_t> readyTime() const;
+
std::optional<nsecs_t> targetVsync() const;
// This moves state from armed->disarmed.
@@ -67,14 +69,14 @@
// Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
// call to update()
- void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync);
+ void addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming);
// Checks if there is a pending update to the workload, returning true if so.
bool hasPendingWorkloadUpdate() const;
// End: functions that are not threadsafe.
// Invoke the callback with the two given timestamps, moving the state from running->disarmed.
- void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
+ void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp, nsecs_t deadlineTimestamp);
// Block calling thread while the callback is executing.
void ensureNotRunning();
@@ -84,22 +86,18 @@
std::string const mName;
VSyncDispatch::Callback const mCallback;
- nsecs_t mWorkDuration;
- nsecs_t mEarliestVsync;
+ VSyncDispatch::ScheduleTiming mScheduleTiming;
nsecs_t const mMinVsyncDistance;
struct ArmingInfo {
nsecs_t mActualWakeupTime;
nsecs_t mActualVsyncTime;
+ nsecs_t mActualReadyTime;
};
std::optional<ArmingInfo> mArmedInfo;
std::optional<nsecs_t> mLastDispatchTime;
- struct WorkloadUpdateInfo {
- nsecs_t duration;
- nsecs_t earliestVsync;
- };
- std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo;
+ std::optional<VSyncDispatch::ScheduleTiming> mWorkloadUpdateInfo;
mutable std::mutex mRunningMutex;
std::condition_variable mCv;
@@ -125,7 +123,7 @@
CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
void unregisterCallback(CallbackToken token) final;
- ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+ ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) final;
CancelResult cancel(CallbackToken token) final;
void dump(std::string& result) const final;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 61f3fbb..e90edf7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -14,10 +14,6 @@
* 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include "VSyncPredictor.h"
@@ -54,7 +50,7 @@
}
}
-inline size_t VSyncPredictor::next(int i) const {
+inline size_t VSyncPredictor::next(size_t i) const {
return (i + 1) % mTimestamps.size();
}
@@ -69,12 +65,12 @@
}
nsecs_t VSyncPredictor::currentPeriod() const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
return std::get<0>(mRateMap.find(mIdealPeriod)->second);
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
if (!validate(timestamp)) {
// VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
@@ -138,14 +134,14 @@
auto meanTS = scheduler::calculate_mean(vsyncTS);
auto meanOrdinal = scheduler::calculate_mean(ordinals);
- for (auto i = 0; i < vsyncTS.size(); i++) {
+ for (size_t i = 0; i < vsyncTS.size(); i++) {
vsyncTS[i] -= meanTS;
ordinals[i] -= meanOrdinal;
}
auto top = 0ll;
auto bottom = 0ll;
- for (auto i = 0; i < vsyncTS.size(); i++) {
+ for (size_t i = 0; i < vsyncTS.size(); i++) {
top += vsyncTS[i] * ordinals[i];
bottom += ordinals[i] * ordinals[i];
}
@@ -177,9 +173,9 @@
}
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
- auto const [slope, intercept] = getVSyncPredictionModel(lk);
+ auto const [slope, intercept] = getVSyncPredictionModel(lock);
if (mTimestamps.empty()) {
traceInt64If("VSP-mode", 1);
@@ -215,8 +211,8 @@
}
std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
- std::lock_guard<std::mutex> lk(mMutex);
- return VSyncPredictor::getVSyncPredictionModel(lk);
+ std::lock_guard lock(mMutex);
+ return VSyncPredictor::getVSyncPredictionModel(lock);
}
std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
@@ -227,7 +223,7 @@
void VSyncPredictor::setPeriod(nsecs_t period) {
ATRACE_CALL();
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
static constexpr size_t kSizeLimit = 30;
if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
mRateMap.erase(mRateMap.begin());
@@ -256,18 +252,18 @@
}
bool VSyncPredictor::needsMoreSamples() const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
return mTimestamps.size() < kMinimumSamplesForPrediction;
}
void VSyncPredictor::resetModel() {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
clearTimestamps();
}
void VSyncPredictor::dump(std::string& result) const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
StringAppendF(&result, "\tRefresh Rate Map:\n");
for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
@@ -280,5 +276,3 @@
} // namespace android::scheduler
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 5f3c418..5f2ec49 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -74,7 +74,7 @@
size_t const kOutlierTolerancePercent;
std::mutex mutable mMutex;
- size_t next(int i) const REQUIRES(mMutex);
+ size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
REQUIRES(mMutex);
@@ -84,7 +84,7 @@
std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
- int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+ size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
};
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index a2b279b..7b5d462 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -30,136 +30,23 @@
namespace android::scheduler {
using base::StringAppendF;
+VsyncController::~VsyncController() = default;
+
Clock::~Clock() = default;
nsecs_t SystemClock::now() const {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-class PredictedVsyncTracer {
-public:
- PredictedVsyncTracer(VSyncDispatch& dispatch)
- : mRegistration(dispatch,
- std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1,
- std::placeholders::_2),
- "PredictedVsyncTracer") {
- mRegistration.schedule(0, 0);
- }
-
-private:
- TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
- VSyncCallbackRegistration mRegistration;
-
- void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) {
- mParity = !mParity;
- mRegistration.schedule(0, 0);
- }
-};
-
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncDispatch& dispatch,
- VSyncTracker& tracker, size_t pendingFenceLimit,
- bool supportKernelIdleTimer)
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker,
+ size_t pendingFenceLimit, bool supportKernelIdleTimer)
: mClock(std::move(clock)),
mTracker(tracker),
- mDispatch(dispatch),
mPendingLimit(pendingFenceLimit),
- mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
- ? std::make_unique<PredictedVsyncTracer>(mDispatch)
- : nullptr),
mSupportKernelIdleTimer(supportKernelIdleTimer) {}
VSyncReactor::~VSyncReactor() = default;
-// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
-// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
-// for now.
-class CallbackRepeater {
-public:
- CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
- nsecs_t period, nsecs_t offset, nsecs_t notBefore)
- : mName(name),
- mCallback(cb),
- mRegistration(dispatch,
- std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
- std::placeholders::_2),
- mName),
- mPeriod(period),
- mOffset(offset),
- mLastCallTime(notBefore) {}
-
- ~CallbackRepeater() {
- std::lock_guard<std::mutex> lk(mMutex);
- mRegistration.cancel();
- }
-
- void start(nsecs_t offset) {
- std::lock_guard<std::mutex> lk(mMutex);
- mStopped = false;
- mOffset = offset;
-
- auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
- LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
- "Error scheduling callback: rc %X", schedule_result);
- }
-
- void setPeriod(nsecs_t period) {
- std::lock_guard<std::mutex> lk(mMutex);
- if (period == mPeriod) {
- return;
- }
- mPeriod = period;
- }
-
- void stop() {
- std::lock_guard<std::mutex> lk(mMutex);
- LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
- mStopped = true;
- mRegistration.cancel();
- }
-
- void dump(std::string& result) const {
- std::lock_guard<std::mutex> lk(mMutex);
- StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
- mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
- mStopped ? "stopped" : "running");
- }
-
-private:
- void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
- {
- std::lock_guard<std::mutex> lk(mMutex);
- mLastCallTime = vsynctime;
- }
-
- mCallback->onDispSyncEvent(wakeupTime, vsynctime);
-
- {
- std::lock_guard<std::mutex> lk(mMutex);
- if (mStopped) {
- return;
- }
- auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
- LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
- "Error rescheduling callback: rc %X", schedule_result);
- }
- }
-
- // DispSync offsets are defined as time after the vsync before presentation.
- // VSyncReactor workloads are defined as time before the intended presentation vsync.
- // Note change in sign between the two defnitions.
- nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
-
- const std::string mName;
- DispSync::Callback* const mCallback;
-
- std::mutex mutable mMutex;
- VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
- bool mStopped GUARDED_BY(mMutex) = false;
- nsecs_t mPeriod GUARDED_BY(mMutex);
- nsecs_t mOffset GUARDED_BY(mMutex);
- nsecs_t mLastCallTime GUARDED_BY(mMutex);
-};
-
-bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
if (!fence) {
return false;
}
@@ -169,7 +56,7 @@
return true;
}
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
if (mExternalIgnoreFences || mInternalIgnoreFences) {
return true;
}
@@ -206,14 +93,14 @@
return mMoreSamplesNeeded;
}
-void VSyncReactor::setIgnorePresentFences(bool ignoration) {
- std::lock_guard<std::mutex> lk(mMutex);
- mExternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFences(bool ignore) {
+ std::lock_guard lock(mMutex);
+ mExternalIgnoreFences = ignore;
updateIgnorePresentFencesInternal();
}
-void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
- mInternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignore) {
+ mInternalIgnoreFences = ignore;
updateIgnorePresentFencesInternal();
}
@@ -223,16 +110,7 @@
}
}
-nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
- auto const currentPeriod = periodOffset ? mTracker.currentPeriod() : 0;
- return mTracker.nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
-}
-
-nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
- return mTracker.nextAnticipatedVSyncTimeFrom(now);
-}
-
-void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
ATRACE_CALL();
mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
@@ -247,30 +125,20 @@
mLastHwVsync.reset();
}
-void VSyncReactor::setPeriod(nsecs_t period) {
+void VSyncReactor::startPeriodTransition(nsecs_t period) {
ATRACE_INT64("VSR-setPeriod", period);
- std::lock_guard lk(mMutex);
+ std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && period == getPeriod()) {
+ if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
} else {
- startPeriodTransition(period);
+ startPeriodTransitionInternal(period);
}
}
-nsecs_t VSyncReactor::getPeriod() {
- return mTracker.currentPeriod();
-}
-
-void VSyncReactor::beginResync() {
- mTracker.resetModel();
-}
-
-void VSyncReactor::endResync() {}
-
bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
if (!mPeriodConfirmationInProgress) {
return false;
@@ -281,13 +149,13 @@
}
const bool periodIsChanging =
- mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod());
+ mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod());
if (mSupportKernelIdleTimer && !periodIsChanging) {
// Clear out the Composer-provided period and use the allowance logic below
HwcVsyncPeriod = {};
}
- auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+ auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod();
static constexpr int allowancePercent = 10;
static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -299,18 +167,15 @@
return std::abs(distance - period) < allowance;
}
-bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) {
+bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) {
assert(periodFlushed);
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
ATRACE_NAME("VSR: period confirmed");
if (mPeriodTransitioningTo) {
mTracker.setPeriod(*mPeriodTransitioningTo);
- for (auto& entry : mCallbacks) {
- entry.second->setPeriod(*mPeriodTransitioningTo);
- }
*periodFlushed = true;
}
@@ -339,51 +204,8 @@
return mMoreSamplesNeeded;
}
-status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
- DispSync::Callback* callback,
- nsecs_t /* lastCallbackTime */) {
- std::lock_guard<std::mutex> lk(mMutex);
- auto it = mCallbacks.find(callback);
- if (it == mCallbacks.end()) {
- // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
- static auto constexpr maxListeners = 4;
- if (mCallbacks.size() >= maxListeners) {
- ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
- maxListeners, mCallbacks.size());
- return NO_MEMORY;
- }
-
- auto const period = mTracker.currentPeriod();
- auto repeater = std::make_unique<CallbackRepeater>(mDispatch, callback, name, period, phase,
- mClock->now());
- it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
- }
-
- it->second->start(phase);
- return NO_ERROR;
-}
-
-status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
- nsecs_t* /* outLastCallback */) {
- std::lock_guard<std::mutex> lk(mMutex);
- auto const it = mCallbacks.find(callback);
- LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
-
- it->second->stop();
- return NO_ERROR;
-}
-
-status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
- std::lock_guard<std::mutex> lk(mMutex);
- auto const it = mCallbacks.find(callback);
- LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
-
- it->second->start(phase);
- return NO_ERROR;
-}
-
void VSyncReactor::dump(std::string& result) const {
- std::lock_guard<std::mutex> lk(mMutex);
+ std::lock_guard lock(mMutex);
StringAppendF(&result, "VsyncReactor in use\n");
StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
@@ -403,17 +225,8 @@
StringAppendF(&result, "No Last HW vsync\n");
}
- StringAppendF(&result, "CallbackRepeaters:\n");
- for (const auto& [callback, repeater] : mCallbacks) {
- repeater->dump(result);
- }
-
StringAppendF(&result, "VSyncTracker:\n");
mTracker.dump(result);
- StringAppendF(&result, "VSyncDispatch:\n");
- mDispatch.dump(result);
}
-void VSyncReactor::reset() {}
-
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 22ceb39..449d4c3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,73 +22,53 @@
#include <mutex>
#include <unordered_map>
#include <vector>
-#include "DispSync.h"
#include "TimeKeeper.h"
+#include "VsyncController.h"
namespace android::scheduler {
class Clock;
class VSyncDispatch;
class VSyncTracker;
-class CallbackRepeater;
-class PredictedVsyncTracer;
// TODO (b/145217110): consider renaming.
-class VSyncReactor : public android::DispSync {
+class VSyncReactor : public VsyncController {
public:
- VSyncReactor(std::unique_ptr<Clock> clock, VSyncDispatch& dispatch, VSyncTracker& tracker,
- size_t pendingFenceLimit, bool supportKernelIdleTimer);
+ VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit,
+ bool supportKernelIdleTimer);
~VSyncReactor();
- bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
- void setIgnorePresentFences(bool ignoration) final;
+ bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+ void setIgnorePresentFences(bool ignore) final;
- nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const final;
- nsecs_t expectedPresentTime(nsecs_t now) final;
+ void startPeriodTransition(nsecs_t period) final;
- void setPeriod(nsecs_t period) final;
- nsecs_t getPeriod() final;
-
- // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
- void beginResync() final;
- bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
- bool* periodFlushed) final;
- void endResync() final;
-
- status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
- nsecs_t lastCallbackTime) final;
- status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final;
- status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final;
+ bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) final;
void dump(std::string& result) const final;
- void reset() final;
private:
- void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+ void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex);
void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
- void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
+ void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex);
void endPeriodTransition() REQUIRES(mMutex);
bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
VSyncTracker& mTracker;
- VSyncDispatch& mDispatch;
size_t const mPendingLimit;
mutable std::mutex mMutex;
bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
- std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+ std::vector<std::shared_ptr<android::FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
- std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
- GUARDED_BY(mMutex);
-
- const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
const bool mSupportKernelIdleTimer = false;
};
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
new file mode 100644
index 0000000..0f0df22
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <cstddef>
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <ui/FenceTime.h>
+
+#include <memory>
+
+namespace android::scheduler {
+
+class FenceTime;
+
+class VsyncController {
+public:
+ virtual ~VsyncController();
+
+ /*
+ * Adds a present fence to the model. The controller will use the fence time as
+ * a vsync signal.
+ *
+ * \param [in] fence The present fence given from the display
+ * \return True if the model needs more vsync signals to make
+ * an accurate prediction,
+ * False otherwise
+ */
+ virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+
+ /*
+ * Adds a hw sync timestamp to the model. The controller will use the timestamp
+ * time as a vsync signal.
+ *
+ * \param [in] timestamp The HW Vsync timestamp
+ * \param [in] hwcVsyncPeriod The Vsync period reported by composer, if available
+ * \param [out] periodFlushed True if the vsync period changed is completed
+ * \return True if the model needs more vsync signals to make
+ * an accurate prediction,
+ * False otherwise
+ */
+ virtual bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+ bool* periodFlushed) = 0;
+
+ /*
+ * Inform the controller that the period is changing and the controller needs to recalibrate
+ * itself. The controller will end the period transition internally.
+ *
+ * \param [in] period The period that the system is changing into.
+ */
+ virtual void startPeriodTransition(nsecs_t period) = 0;
+
+ /*
+ * Tells the tracker to stop using present fences to get a vsync signal.
+ *
+ * \param [in] ignore Whether to ignore the present fences or not
+ */
+ virtual void setIgnorePresentFences(bool ignore) = 0;
+
+ virtual void dump(std::string& result) const = 0;
+
+protected:
+ VsyncController() = default;
+ VsyncController(VsyncController const&) = delete;
+ VsyncController& operator=(VsyncController const&) = delete;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 884e7d2..2ff26a9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -48,11 +48,9 @@
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <dlfcn.h>
-#include <dvr/vr_flinger.h>
#include <errno.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
-#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/LayerDebugInfo.h>
@@ -75,7 +73,6 @@
#include <ui/DisplayState.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
-#include <ui/UiConfig.h>
#include <utils/StopWatch.h>
#include <utils/String16.h>
#include <utils/String8.h>
@@ -108,6 +105,7 @@
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
#include "LayerRenderArea.h"
@@ -117,13 +115,13 @@
#include "Promise.h"
#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
-#include "Scheduler/DispSync.h"
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
@@ -285,7 +283,6 @@
bool SurfaceFlinger::useHwcForRgbToYuv;
uint64_t SurfaceFlinger::maxVirtualDisplaySize;
bool SurfaceFlinger::hasSyncFramework;
-bool SurfaceFlinger::useVrFlinger;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
@@ -331,9 +328,10 @@
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
- mInterceptor(mFactory.createSurfaceInterceptor(this)),
+ mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(std::make_unique<FrameTracer>()),
+ mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -352,9 +350,6 @@
maxVirtualDisplaySize = max_virtual_display_dimension(0);
- // Vr flinger is only enabled on Daydream ready devices.
- useVrFlinger = use_vr_flinger(false);
-
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
@@ -509,6 +504,15 @@
}
sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {
+ // onTransact already checks for some permissions, but adding an additional check here.
+ // This is to ensure that only system and graphics can request to create a secure
+ // display. Secure displays can show secure content so we add an additional restriction on it.
+ const int uid = IPCThreadState::self()->getCallingUid();
+ if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
+ ALOGE("Only privileged processes can create a secure display");
+ return nullptr;
+ }
+
class DisplayToken : public BBinder {
sp<SurfaceFlinger> flinger;
virtual ~DisplayToken() {
@@ -624,10 +628,6 @@
mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
- if (mVrFlinger) {
- mVrFlinger->OnBootFinished();
- }
-
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
@@ -711,43 +711,16 @@
: renderengine::RenderEngine::ContextPriority::MEDIUM)
.build()));
mCompositionEngine->setTimeStats(mTimeStats);
-
- LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
- "Starting with vr flinger active is not currently supported.");
mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();
const auto display = getDefaultDisplayDeviceLocked();
LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
- LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
+ const auto displayId = display->getPhysicalId();
+ LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
"Internal display is disconnected.");
- if (useVrFlinger) {
- auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) {
- // This callback is called from the vr flinger dispatch thread. We
- // need to call signalTransaction(), which requires holding
- // mStateLock when we're not on the main thread. Acquiring
- // mStateLock from the vr flinger dispatch thread might trigger a
- // deadlock in surface flinger (see b/66916578), so post a message
- // to be handled on the main thread instead.
- static_cast<void>(schedule([=] {
- ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
- mVrFlingerRequestsDisplay = requestDisplay;
- signalTransaction();
- }));
- };
- auto hwcDisplayId =
- getHwComposer()
- .fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*display->getId()))
- .value_or(0);
- mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(), hwcDisplayId,
- vrFlingerRequestDisplayCallback);
- if (!mVrFlinger) {
- ALOGE("Failed to start vrflinger");
- }
- }
-
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -961,7 +934,7 @@
//
// Normally it's one full refresh period (to give SF a chance to
// latch the buffer), but this can be reduced by configuring a
- // DispSync offset. Any additional delays introduced by the hardware
+ // VsyncController offset. Any additional delays introduced by the hardware
// composer or panel must be accounted for here.
//
// We add an additional 1ms to allow for processing time and
@@ -979,7 +952,7 @@
return BAD_VALUE;
}
- mScheduler->getDisplayStatInfo(stats);
+ mScheduler->getDisplayStatInfo(stats, systemTime());
return NO_ERROR;
}
@@ -1037,7 +1010,7 @@
// switch.
mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
- // DispSync model is locked.
+ // VsyncController model is locked.
modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
updatePhaseConfiguration(refreshRate);
@@ -1106,8 +1079,8 @@
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
.getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
- static_cast<PhysicalDisplayId>(*display->getId()),
+ const auto physicalId = display->getPhysicalId();
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
mUpcomingActiveConfig.configId, vsyncPeriod);
}
}
@@ -1156,8 +1129,7 @@
}
mUpcomingActiveConfig = *desiredActiveConfig;
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
+ const auto displayId = display->getPhysicalId();
ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getFps());
@@ -1168,7 +1140,7 @@
hal::VsyncPeriodChangeTimeline outTimeline;
auto status =
- getHwComposer().setActiveConfigWithConstraints(*displayId,
+ getHwComposer().setActiveConfigWithConstraints(displayId,
mUpcomingActiveConfig.configId.value(),
constraints, &outTimeline);
if (status != NO_ERROR) {
@@ -1442,8 +1414,8 @@
Mutex::Autolock lock(mStateLock);
if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mEventQueue->setEventConnection(
- mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
+ mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
+ : nullptr);
}
}).wait();
@@ -1452,7 +1424,11 @@
status_t SurfaceFlinger::injectVSync(nsecs_t when) {
Mutex::Autolock lock(mStateLock);
- return mScheduler->injectVSync(when, calculateExpectedPresentTime(when)) ? NO_ERROR : BAD_VALUE;
+ const auto expectedPresent = calculateExpectedPresentTime(when);
+ return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
+ /*deadlineTimestamp=*/expectedPresent)
+ ? NO_ERROR
+ : BAD_VALUE;
}
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
@@ -1689,103 +1665,11 @@
if (const auto display = getDefaultDisplayDeviceLocked();
display && display->isPoweredOn()) {
- getHwComposer().setVsyncEnabled(*display->getId(), mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
}
}));
}
-void SurfaceFlinger::resetDisplayState() {
- mScheduler->disableHardwareVsync(true);
- // Clear the drawing state so that the logic inside of
- // handleTransactionLocked will fire. It will determine the delta between
- // mCurrentState and mDrawingState and re-apply all changes when we make the
- // transition.
- mDrawingState.displays.clear();
- mDisplays.clear();
-}
-
-void SurfaceFlinger::updateVrFlinger() {
- ATRACE_CALL();
- if (!mVrFlinger)
- return;
- bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay;
- if (vrFlingerRequestsDisplay == getHwComposer().isUsingVrComposer()) {
- return;
- }
-
- if (vrFlingerRequestsDisplay && !getHwComposer().getComposer()->isRemote()) {
- ALOGE("Vr flinger is only supported for remote hardware composer"
- " service connections. Ignoring request to transition to vr"
- " flinger.");
- mVrFlingerRequestsDisplay = false;
- return;
- }
-
- Mutex::Autolock _l(mStateLock);
-
- sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
- LOG_ALWAYS_FATAL_IF(!display);
-
- const hal::PowerMode currentDisplayPowerMode = display->getPowerMode();
-
- // Clear out all the output layers from the composition engine for all
- // displays before destroying the hardware composer interface. This ensures
- // any HWC layers are destroyed through that interface before it becomes
- // invalid.
- for (const auto& [token, displayDevice] : mDisplays) {
- displayDevice->getCompositionDisplay()->clearOutputLayers();
- }
-
- // This DisplayDevice will no longer be relevant once resetDisplayState() is
- // called below. Clear the reference now so we don't accidentally use it
- // later.
- display.clear();
-
- if (!vrFlingerRequestsDisplay) {
- mVrFlinger->SeizeDisplayOwnership();
- }
-
- resetDisplayState();
- // Delete the current instance before creating the new one
- mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
- mCompositionEngine->setHwComposer(getFactory().createHWComposer(
- vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
- mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId);
-
- LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
- "Switched to non-remote hardware composer");
-
- if (vrFlingerRequestsDisplay) {
- mVrFlinger->GrantDisplayOwnership();
- }
-
- mVisibleRegionsDirty = true;
- invalidateHwcGeometry();
-
- // Re-enable default display.
- display = getDefaultDisplayDeviceLocked();
- LOG_ALWAYS_FATAL_IF(!display);
- setPowerModeInternal(display, currentDisplayPowerMode);
-
- // Reset the timing values to account for the period of the swapped in HWC
- const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
- mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-
- // The present fences returned from vr_hwc are not an accurate
- // representation of vsync times.
- mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || !hasSyncFramework);
-
- // Use phase of 0 since phase is not known.
- // Use latency of 0, which will snap to the ideal latency.
- DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
- setCompositorTimingSnapped(stats, 0);
-
- mScheduler->resyncToHardwareVsync(false, vsyncPeriod);
-
- mRepaintEverything = true;
- setTransactionFlags(eDisplayTransactionNeeded);
-}
-
sp<Fence> SurfaceFlinger::previousFrameFence() {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
@@ -1820,18 +1704,17 @@
nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const {
DisplayStatInfo stats;
- mScheduler->getDisplayStatInfo(&stats);
- const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
+ mScheduler->getDisplayStatInfo(&stats, now);
// Inflate the expected present time if we're targetting the next vsync.
- return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? presentTime
- : presentTime + stats.vsyncPeriod;
+ return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
+ : stats.vsyncTime + stats.vsyncPeriod;
}
-void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
- onMessageInvalidate(expectedVSyncTime);
+ onMessageInvalidate(vsyncId, expectedVSyncTime);
break;
}
case MessageQueue::REFRESH: {
@@ -1841,7 +1724,7 @@
}
}
-void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
ATRACE_CALL();
const nsecs_t frameStart = systemTime();
@@ -1870,7 +1753,7 @@
// smaller than a typical frame duration, but should not be so small
// that it reports reasonable drift as a missed frame.
DisplayStatInfo stats;
- mScheduler->getDisplayStatInfo(&stats);
+ mScheduler->getDisplayStatInfo(&stats, systemTime());
const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
const nsecs_t previousPresentTime = previousFramePresentTime();
const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
@@ -1952,11 +1835,6 @@
}
}
- // Now that we're going to make it to the handleMessageTransaction()
- // call below it's safe to call updateVrFlinger(), which will
- // potentially trigger a display handoff.
- updateVrFlinger();
-
if (mTracingEnabledChanged) {
mTracingEnabled = mTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -1972,6 +1850,8 @@
{
ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
+ mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
+
refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
if (mTracingEnabled) {
@@ -2054,7 +1934,7 @@
refreshArgs.layers.push_back(layerFE);
});
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (sp<Layer> layer : mLayersWithQueuedFrames) {
+ for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
@@ -2099,7 +1979,10 @@
postFrame();
postComposition();
- const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
+ mFrameTimeline->setSfPresent(systemTime(),
+ std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+
+ const bool prevFrameHadClientComposition = mHadClientComposition;
mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
const auto& state = pair.second->getCompositionDisplay()->getState();
@@ -2114,9 +1997,8 @@
const auto& state = pair.second->getCompositionDisplay()->getState();
return state.reusedClientComposition;
});
-
- // Only report a strategy change if we move in and out of composition with hw overlays
- if (prevFrameHadDeviceComposition != mHadDeviceComposition) {
+ // Only report a strategy change if we move in and out of client composition
+ if (prevFrameHadClientComposition != mHadClientComposition) {
mTimeStats->incrementCompositionStrategyChanges();
}
@@ -2217,7 +2099,7 @@
ALOGV("postComposition");
nsecs_t dequeueReadyTime = systemTime();
- for (auto& layer : mLayersWithQueuedFrames) {
+ for (auto layer : mLayersWithQueuedFrames) {
layer->releasePendingBuffer(dequeueReadyTime);
}
@@ -2238,12 +2120,12 @@
getBE().mDisplayTimeline.updateSignalTimes();
mPreviousPresentFences[1] = mPreviousPresentFences[0];
mPreviousPresentFences[0] =
- display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
+ display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
getBE().mDisplayTimeline.push(presentFenceTime);
DisplayStatInfo stats;
- mScheduler->getDisplayStatInfo(&stats);
+ mScheduler->getDisplayStatInfo(&stats, systemTime());
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
@@ -2272,7 +2154,8 @@
mScheduler->addPresentFence(presentFenceTime);
}
- const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+ const bool isDisplayConnected =
+ display && getHwComposer().isConnected(display->getPhysicalId());
if (!hasSyncFramework) {
if (isDisplayConnected && display->isPoweredOn()) {
@@ -2289,7 +2172,8 @@
} else if (isDisplayConnected) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
- const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
+ const nsecs_t presentTime =
+ getHwComposer().getRefreshTimestamp(display->getPhysicalId());
mAnimFrameTracker.setActualPresentTime(presentTime);
}
mAnimFrameTracker.advanceFrame();
@@ -2391,7 +2275,7 @@
void SurfaceFlinger::postFrame() {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- if (display && getHwComposer().isConnected(*display->getId())) {
+ if (display && getHwComposer().isConnected(display->getPhysicalId())) {
uint32_t flipCount = display->getPageFlipCount();
if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
logFrameStats();
@@ -2496,7 +2380,6 @@
const DisplayDeviceState& state,
const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) {
- auto displayId = compositionDisplay->getDisplayId();
DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
creationArgs.isSecure = state.isSecure;
@@ -2508,26 +2391,26 @@
creationArgs.connectionType = physical->type;
}
- const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
- creationArgs.isPrimary = isInternalDisplay;
+ if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+ creationArgs.isPrimary = id == getInternalDisplayIdLocked();
- if (useColorManagement && displayId) {
- std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
- for (ColorMode colorMode : modes) {
- if (isWideColorMode(colorMode)) {
- creationArgs.hasWideColorGamut = true;
+ 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);
}
-
- std::vector<RenderIntent> renderIntents =
- getHwComposer().getRenderIntents(*displayId, colorMode);
- creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
}
}
- if (displayId) {
- getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
- creationArgs.supportedPerFrameMetadata =
- getHwComposer().getSupportedPerFrameMetadata(*displayId);
+ if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+ getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
+ creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
}
auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
@@ -2542,7 +2425,7 @@
}
creationArgs.physicalOrientation =
- isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0;
+ creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0;
// virtual displays are always considered enabled
creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
@@ -2564,8 +2447,8 @@
RenderIntent::COLORIMETRIC,
Dataspace::UNKNOWN});
if (!state.isVirtual()) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+ const auto physicalId = display->getPhysicalId();
+ auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(physicalId));
display->setActiveConfig(activeConfigId);
display->setDeviceProductInfo(state.physical->deviceProductInfo);
}
@@ -2614,7 +2497,8 @@
builder.setIsSecure(state.isSecure);
builder.setLayerStackId(state.layerStack);
builder.setPowerAdvisor(&mPowerAdvisor);
- builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+ builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays);
+ builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
builder.setName(state.displayName);
const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -2624,11 +2508,13 @@
sp<IGraphicBufferConsumer> bqConsumer;
getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
- std::optional<DisplayId> displayId = compositionDisplay->getId();
+ DisplayId displayId = compositionDisplay->getId();
if (state.isVirtual()) {
+ const auto virtualId = VirtualDisplayId::tryCast(displayId);
+ LOG_FATAL_IF(!virtualId);
sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+ new VirtualDisplaySurface(getHwComposer(), *virtualId, state.surface, bqProducer,
bqConsumer, state.displayName);
displaySurface = vds;
@@ -2638,9 +2524,9 @@
"adding a supported display, but rendering "
"surface is provided (%p), ignoring it",
state.surface.get());
-
- LOG_ALWAYS_FATAL_IF(!displayId);
- displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+ const auto physicalId = PhysicalDisplayId::tryCast(displayId);
+ LOG_FATAL_IF(!physicalId);
+ displaySurface = new FramebufferSurface(getHwComposer(), *physicalId, bqConsumer,
maxGraphicsWidth, maxGraphicsHeight);
producer = bqProducer;
}
@@ -2650,8 +2536,7 @@
displaySurface, producer);
mDisplays.emplace(displayToken, display);
if (!state.isVirtual()) {
- LOG_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), true);
+ dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
}
if (display->isPrimary()) {
@@ -2661,13 +2546,9 @@
void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
- // Save display ID before disconnecting.
- const auto displayId = display->getId();
display->disconnect();
-
if (!display->isVirtual()) {
- LOG_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), false);
+ dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
}
}
@@ -2681,6 +2562,7 @@
const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
// changing the surface is like destroying and recreating the DisplayDevice
+ getRenderEngine().cleanFramebufferCache();
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
}
@@ -2912,6 +2794,9 @@
setInputWindowsFinished();
}
+ for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
+ mInputFlinger->setFocusedWindow(focusRequest);
+ }
mInputWindowCommands.clear();
}
@@ -2929,10 +2814,6 @@
mInputFlinger->setInputWindows(inputInfos,
mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
: nullptr);
- for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
- mInputFlinger->setFocusedWindow(focusRequest);
- }
- mInputWindowCommands.focusRequests.clear();
}
void SurfaceFlinger::commitInputWindowCommands() {
@@ -2943,7 +2824,7 @@
void SurfaceFlinger::updateCursorAsync() {
compositionengine::CompositionRefreshArgs refreshArgs;
for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
- if (display->getId()) {
+ if (HalDisplayId::tryCast(display->getId())) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
@@ -2984,18 +2865,24 @@
// start the EventThread
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
+ const auto configs = mVsyncConfiguration->getCurrentConfigs();
+ const nsecs_t vsyncPeriod =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
mAppConnectionHandle =
- mScheduler->createConnection("app",
- mVsyncConfiguration->getCurrentConfigs().late.appOffset,
+ mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
+ /*workDuration=*/configs.late.appWorkDuration,
+ /*readyDuration=*/configs.late.sfWorkDuration,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf",
- mVsyncConfiguration->getCurrentConfigs().late.sfOffset,
+ mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
+ /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+ /*readyDuration=*/configs.late.sfWorkDuration,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
- mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+ mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+ configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -3007,8 +2894,6 @@
// 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.
- const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
vsyncPeriod);
static auto ignorePresentFences =
@@ -3020,12 +2905,19 @@
void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()));
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+ refreshRate.getVsyncPeriod());
}
-void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, config.appOffset);
- mScheduler->setPhaseOffset(mSfConnectionHandle, config.sfOffset);
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
+ nsecs_t vsyncPeriod) {
+ mScheduler->setDuration(mAppConnectionHandle,
+ /*workDuration=*/config.appWorkDuration,
+ /*readyDuration=*/config.sfWorkDuration);
+ mScheduler->setDuration(mSfConnectionHandle,
+ /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+ /*readyDuration=*/config.sfWorkDuration);
+ mEventQueue->setDuration(config.sfWorkDuration);
}
void SurfaceFlinger::commitTransaction() {
@@ -3129,7 +3021,7 @@
if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
- mLayersWithQueuedFrames.push_back(layer);
+ mLayersWithQueuedFrames.emplace(layer);
} else {
ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
@@ -3307,12 +3199,13 @@
break;
}
transactions.push_back(transaction);
- applyTransactionState(transaction.states, transaction.displays, transaction.flags,
+ applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+ transaction.displays, transaction.flags,
mPendingInputWindowCommands, transaction.desiredPresentTime,
transaction.buffer, transaction.postTime,
transaction.privileged, transaction.hasListenerCallbacks,
- transaction.listenerCallbacks, transaction.originPID,
- transaction.originUID, /*isMainThread*/ true);
+ transaction.listenerCallbacks, transaction.originPid,
+ transaction.originUid, transaction.id, /*isMainThread*/ true);
transactionQueue.pop();
flushedATransaction = true;
}
@@ -3356,11 +3249,12 @@
return true;
}
-void SurfaceFlinger::setTransactionState(
- const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) {
+status_t SurfaceFlinger::setTransactionState(
+ int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
ATRACE_CALL();
const int64_t postTime = systemTime();
@@ -3393,29 +3287,32 @@
}
IPCThreadState* ipc = IPCThreadState::self();
- const int originPID = ipc->getCallingPid();
- const int originUID = ipc->getCallingUid();
+ const int originPid = ipc->getCallingPid();
+ const int originUid = ipc->getCallingUid();
if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
- mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
- uncacheBuffer, postTime, privileged,
- hasListenerCallbacks, listenerCallbacks, originPID,
- originUID);
+ mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
+ desiredPresentTime, uncacheBuffer, postTime,
+ privileged, hasListenerCallbacks, listenerCallbacks,
+ originPid, originUid, transactionId);
setTransactionFlags(eTransactionFlushNeeded);
- return;
+ return NO_ERROR;
}
- applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
- uncacheBuffer, postTime, privileged, hasListenerCallbacks,
- listenerCallbacks, originPID, originUID, /*isMainThread*/ false);
+ applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
+ desiredPresentTime, uncacheBuffer, postTime, privileged,
+ hasListenerCallbacks, listenerCallbacks, originPid, originUid,
+ transactionId, /*isMainThread*/ false);
+ return NO_ERROR;
}
void SurfaceFlinger::applyTransactionState(
- const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+ int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- int originPID, int originUID, bool isMainThread) {
+ int originPid, int originUid, uint64_t transactionId, bool isMainThread) {
uint32_t transactionFlags = 0;
if (flags & eAnimation) {
@@ -3449,8 +3346,9 @@
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
- listenerCallbacksWithSurfaces);
+ clientStateFlags |=
+ setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime,
+ privileged, listenerCallbacksWithSurfaces);
if ((flags & eAnimation) && state.state.surface) {
if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
@@ -3469,7 +3367,11 @@
}
transactionFlags |= clientStateFlags;
- transactionFlags |= addInputWindowCommands(inputWindowCommands);
+ if (privileged) {
+ transactionFlags |= addInputWindowCommands(inputWindowCommands);
+ } else if (!inputWindowCommands.empty()) {
+ ALOGE("Only privileged callers are allowed to send input commands.");
+ }
if (uncacheBuffer.isValid()) {
ClientCache::getInstance().erase(uncacheBuffer);
@@ -3502,7 +3404,7 @@
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
- originPID, originUID);
+ originPid, originUid, transactionId);
}
// TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
@@ -3623,8 +3525,8 @@
}
uint32_t SurfaceFlinger::setClientStateLocked(
- const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
- bool privileged,
+ int64_t frameTimelineVsyncId, const ComposerState& composerState,
+ int64_t desiredPresentTime, int64_t postTime, bool privileged,
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
const layer_state_t& s = composerState.state;
@@ -3693,7 +3595,8 @@
const auto& p = layer->getParent();
if (p == nullptr) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setRelativeLayer(s.relativeLayerHandle, s.z) && idx >= 0) {
+ if (layer->setRelativeLayer(s.relativeLayerSurfaceControl->getHandle(), s.z) &&
+ idx >= 0) {
mCurrentState.layersSortedByZ.removeAt(idx);
mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
@@ -3701,7 +3604,7 @@
flags |= eTransactionNeeded|eTraversalNeeded;
}
} else {
- if (p->setChildRelativeLayer(layer, s.relativeLayerHandle, s.z)) {
+ if (p->setChildRelativeLayer(layer, s.relativeLayerSurfaceControl->getHandle(), s.z)) {
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -3787,24 +3690,13 @@
}
}
if (what & layer_state_t::eDeferTransaction_legacy) {
- if (s.barrierHandle_legacy != nullptr) {
- layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy);
- } else if (s.barrierGbp_legacy != nullptr) {
- const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy;
- if (authenticateSurfaceTextureLocked(gbp)) {
- const auto& otherLayer =
- (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy);
- } else {
- ALOGE("Attempt to defer transaction to to an"
- " unrecognized GraphicBufferProducer");
- }
- }
+ layer->deferTransactionUntil_legacy(s.barrierSurfaceControl_legacy->getHandle(),
+ s.barrierFrameNumber);
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
if (what & layer_state_t::eReparentChildren) {
- if (layer->reparentChildren(s.reparentHandle)) {
+ if (layer->reparentChildren(s.reparentSurfaceControl->getHandle())) {
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
@@ -3891,7 +3783,10 @@
// lose its relative z order.
if (what & layer_state_t::eReparent) {
bool hadParent = layer->hasParent();
- if (layer->reparent(s.parentHandleForChild)) {
+ auto parentHandle = (s.parentSurfaceControlForChild)
+ ? s.parentSurfaceControlForChild->getHandle()
+ : nullptr;
+ if (layer->reparent(parentHandle)) {
if (!hadParent) {
mCurrentState.layersSortedByZ.remove(layer);
}
@@ -3925,11 +3820,19 @@
buffer = s.buffer;
}
if (buffer) {
- if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
- s.cachedBuffer)) {
+ const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
+ const uint64_t frameNumber = frameNumberChanged
+ ? s.frameNumber
+ : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
+
+ if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer,
+ frameNumber)) {
flags |= eTraversalNeeded;
}
}
+
+ layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime);
+
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
@@ -3937,12 +3840,12 @@
}
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
- const bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+ bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
return hasChanges ? eTraversalNeeded : 0;
}
status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle) {
+ sp<IBinder>* outHandle, int32_t* outLayerId) {
if (!mirrorFromHandle) {
return NAME_NOT_FOUND;
}
@@ -3967,6 +3870,7 @@
mirrorLayer->mClonedChild = mirrorFrom->createClone();
}
+ *outLayerId = mirrorLayer->sequence;
return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
nullptr /* outTransformHint */);
}
@@ -3975,8 +3879,8 @@
uint32_t h, PixelFormat format, uint32_t flags,
LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
- uint32_t* outTransformHint) {
+ const sp<IBinder>& parentHandle, int32_t* outLayerId,
+ const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
if (int32_t(w|h) < 0) {
ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
int(w), int(h));
@@ -3993,23 +3897,6 @@
std::string uniqueName = getUniqueLayerName(name.string());
- bool primaryDisplayOnly = false;
-
- // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java
- // TODO b/64227542
- if (metadata.has(METADATA_WINDOW_TYPE)) {
- int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0);
- if (windowType == 441731) {
- using U = std::underlying_type_t<InputWindowInfo::Type>;
- // TODO(b/129481165): This static assert can be safely removed once conversion warnings
- // are re-enabled.
- static_assert(std::is_same_v<U, int32_t>);
- metadata.setInt32(METADATA_WINDOW_TYPE,
- static_cast<U>(InputWindowInfo::Type::NAVIGATION_BAR_PANEL));
- primaryDisplayOnly = true;
- }
- }
-
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
@@ -4050,10 +3937,6 @@
return result;
}
- if (primaryDisplayOnly) {
- layer->setPrimaryDisplayOnly();
- }
-
bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
addToCurrentState, outTransformHint);
@@ -4063,6 +3946,7 @@
mInterceptor->saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
+ *outLayerId = layer->sequence;
return result;
}
@@ -4215,8 +4099,9 @@
d.width = 0;
d.height = 0;
displays.add(d);
- setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
- {});
+ setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
+ mPendingInputWindowCommands, -1, {}, false, {},
+ 0 /* Undefined transactionId */);
setPowerModeInternal(display, hal::PowerMode::ON);
const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
@@ -4239,10 +4124,8 @@
return;
}
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
-
- ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+ const auto displayId = display->getPhysicalId();
+ ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
const hal::PowerMode currentMode = display->getPowerMode();
if (mode == currentMode) {
@@ -4259,9 +4142,9 @@
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
- getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
+ getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
}
@@ -4280,14 +4163,14 @@
}
// Make sure HWVsync is disabled before turning off the display
- getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
+ getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE);
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
mVisibleRegionsDirty = true;
// from this point on, SF will stop drawing on this display
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
@@ -4298,10 +4181,10 @@
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
} else {
ALOGE("Attempting to set unknown power mode: %d\n", mode);
- getHwComposer().setPowerMode(*displayId, mode);
+ getHwComposer().setPowerMode(displayId, mode);
}
if (display->isPrimary()) {
@@ -4310,7 +4193,7 @@
mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
}
- ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+ ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
@@ -4341,8 +4224,7 @@
} else {
static const std::unordered_map<std::string, Dumper> dumpers = {
{"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
- {"--dispsync"s,
- dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })},
+ {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
{"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
@@ -4352,6 +4234,7 @@
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
{"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
{"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
};
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4434,6 +4317,10 @@
mTimeStats->parseArgs(asProto, args, result);
}
+void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result) const {
+ mFrameTimeline->parseArgs(args, result);
+}
+
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
@@ -4489,7 +4376,7 @@
}
mScheduler->dump(mAppConnectionHandle, result);
- mScheduler->getPrimaryDispSync().dump(result);
+ mScheduler->dumpVsync(result);
}
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4569,12 +4456,11 @@
void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
+ const auto displayId = PhysicalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
}
- const auto hwcDisplayId =
- getHwComposer().fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*displayId));
+ const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
if (!hwcDisplayId) {
continue;
}
@@ -4627,7 +4513,7 @@
// TODO: print out if wide-color mode is active or not
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
+ const auto displayId = PhysicalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
}
@@ -4716,8 +4602,6 @@
result.append("Build configuration:");
colorizer.reset(result);
appendSfConfigString(result);
- appendUiConfigString(result);
- appendGuiConfigString(result);
result.append("\n");
result.append("\nDisplay identification data:\n");
@@ -4815,9 +4699,10 @@
const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
std::string fps, xDpi, yDpi;
if (activeConfig) {
- fps = std::to_string(1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId)) + " fps";
- xDpi = activeConfig->getDpiX();
- yDpi = activeConfig->getDpiY();
+ fps = base::StringPrintf("%.2f Hz",
+ 1e9f / getHwComposer().getDisplayVsyncPeriod(*displayId));
+ xDpi = base::StringPrintf("%.2f", activeConfig->getDpiX());
+ yDpi = base::StringPrintf("%.2f", activeConfig->getDpiY());
} else {
fps = "unknown";
xDpi = "unknown";
@@ -4842,7 +4727,7 @@
* HWC layer minidump
*/
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
+ const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
}
@@ -4871,15 +4756,6 @@
const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.dump(result);
- /*
- * Dump VrFlinger state if in use.
- */
- if (mVrFlingerRequestsDisplay && mVrFlinger) {
- result.append("VrFlinger state:\n");
- result.append(mVrFlinger->Dump());
- result.append("\n");
- }
-
result.append(mTimeStats->miniDump());
result.append("\n");
}
@@ -4989,7 +4865,8 @@
// captureLayers and captureDisplay will handle the permission check in the function
case CAPTURE_LAYERS:
case CAPTURE_DISPLAY:
- case SET_DISPLAY_BRIGHTNESS: {
+ case SET_DISPLAY_BRIGHTNESS:
+ case SET_FRAME_TIMELINE_VSYNC: {
return OK;
}
@@ -5006,6 +4883,7 @@
}
return OK;
}
+ case ADD_TRANSACTION_TRACE_LISTENER:
case CAPTURE_DISPLAY_BY_ID: {
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
@@ -5024,9 +4902,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1036 are currently used for backdoors. The code
+ // Numbers from 1000 to 1038 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1036) {
+ if (code >= 1000 && code <= 1038) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5169,14 +5047,14 @@
mForceFullDamage = n != 0;
return NO_ERROR;
}
- case 1018: { // Modify Choreographer's phase offset
+ case 1018: { // Modify Choreographer's duration
n = data.readInt32();
- mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n));
+ mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns);
return NO_ERROR;
}
- case 1019: { // Modify SurfaceFlinger's phase offset
+ case 1019: { // Modify SurfaceFlinger's duration
n = data.readInt32();
- mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n));
+ mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
return NO_ERROR;
}
case 1020: { // Layer updates interceptor
@@ -5264,11 +5142,8 @@
}
return NO_ERROR;
}
- // Is VrFlinger active?
- case 1028: {
- Mutex::Autolock _l(mStateLock);
- reply->writeBool(getHwComposer().isUsingVrComposer());
- return NO_ERROR;
+ case 1028: { // Unused.
+ return NAME_NOT_FOUND;
}
// Set buffer size for SF tracing (value in KB)
case 1029: {
@@ -5368,6 +5243,32 @@
}
return NO_ERROR;
}
+ // Inject a hotplug connected event for the primary display. This will deallocate and
+ // reallocate the display state including framebuffers.
+ case 1037: {
+ std::optional<hal::HWDisplayId> hwcId;
+ {
+ Mutex::Autolock lock(mStateLock);
+ hwcId = getHwComposer().getInternalHwcDisplayId();
+ }
+ onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
+ return NO_ERROR;
+ }
+ // Modify the max number of display frames stored within FrameTimeline
+ case 1038: {
+ n = data.readInt32();
+ if (n < 0 || n > MAX_ALLOWED_DISPLAY_FRAMES) {
+ ALOGW("Invalid max size. Maximum allowed is %d", MAX_ALLOWED_DISPLAY_FRAMES);
+ return BAD_VALUE;
+ }
+ if (n == 0) {
+ // restore to default
+ mFrameTimeline->reset();
+ return NO_ERROR;
+ }
+ mFrameTimeline->setMaxDisplayFrames(n);
+ return NO_ERROR;
+ }
}
}
return err;
@@ -5483,7 +5384,7 @@
}
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
status_t validate = validateScreenshotPermissions(args);
@@ -5509,8 +5410,15 @@
reqSize = display->getLayerStackSpaceRect().getSize();
}
- const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
- dataspace = pickDataspaceFromColorMode(colorMode);
+ // 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);
+ }
}
RenderAreaFuture renderAreaFuture = promise::defer([=] {
@@ -5521,8 +5429,9 @@
auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, args.uid, visitor);
};
+
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, captureResults);
+ args.pixelFormat, captureListener);
}
status_t SurfaceFlinger::setSchedFifo(bool enabled) {
@@ -5565,7 +5474,7 @@
}
status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<DisplayDevice> displayWeak;
ui::Size size;
@@ -5586,8 +5495,7 @@
}
RenderAreaFuture renderAreaFuture = promise::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size,
- captureResults.capturedDataspace,
+ return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
false /* useIdentityTransform */,
false /* captureSecureLayers */);
});
@@ -5597,11 +5505,11 @@
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
- ui::PixelFormat::RGBA_8888, captureResults);
+ ui::PixelFormat::RGBA_8888, captureListener);
}
status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
status_t validate = validateScreenshotPermissions(args);
@@ -5667,8 +5575,15 @@
layerStackSpaceRect = display->getLayerStackSpaceRect();
- const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
- dataspace = pickDataspaceFromColorMode(colorMode);
+ // 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);
+ }
captureSecureLayers = args.captureSecureLayers && display->isSecure();
} // mStateLock
@@ -5688,7 +5603,7 @@
captureSecureLayers);
});
- auto traverseLayers = [parent, args, &excludeLayers](const LayerVector::Visitor& visitor) {
+ auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
@@ -5711,13 +5626,13 @@
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, captureResults);
+ args.pixelFormat, captureListener);
}
status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
// TODO(b/116112787) Make buffer usage a parameter.
@@ -5727,54 +5642,56 @@
getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
static_cast<android_pixel_format>(reqPixelFormat),
1 /* layerCount */, usage, "screenshot");
-
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
- false /* regionSampling */, captureResults);
+ false /* regionSampling */, captureListener);
}
status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool regionSampling,
- ScreenCaptureResults& captureResults) {
+ sp<GraphicBuffer>& buffer, bool regionSampling,
+ const sp<IScreenCaptureListener>& captureListener) {
+ ATRACE_CALL();
+
+ if (captureListener == nullptr) {
+ ALOGE("capture screen must provide a capture listener callback");
+ return BAD_VALUE;
+ }
+
const int uid = IPCThreadState::self()->getCallingUid();
const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
- status_t result;
- int syncFd;
+ static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
+ if (mRefreshPending) {
+ ALOGW("Skipping screenshot for now");
+ captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling,
+ captureListener);
+ return;
+ }
+ ScreenCaptureResults captureResults;
+ std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ captureResults.result = NO_MEMORY;
+ captureListener->onScreenCaptureComplete(captureResults);
+ return;
+ }
- do {
- std::tie(result, syncFd) =
- schedule([&]() -> std::pair<status_t, int> {
- if (mRefreshPending) {
- ALOGW("Skipping screenshot for now");
- return {EAGAIN, -1};
- }
- std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- return {NO_MEMORY, -1};
- }
+ status_t result = NO_ERROR;
+ int syncFd = -1;
+ renderArea->render([&] {
+ result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem, &syncFd,
+ regionSampling, captureResults);
+ });
- status_t result = NO_ERROR;
- int fd = -1;
+ if (result == NO_ERROR) {
+ sync_wait(syncFd, -1);
+ close(syncFd);
+ }
+ captureResults.result = result;
+ captureListener->onScreenCaptureComplete(captureResults);
+ }));
- Mutex::Autolock lock(mStateLock);
- renderArea->render([&] {
- result = renderScreenImplLocked(*renderArea, traverseLayers, buffer.get(),
- forSystem, &fd, regionSampling,
- captureResults);
- });
-
- return {result, fd};
- }).get();
- } while (result == EAGAIN);
-
- if (result == NO_ERROR) {
- sync_wait(syncFd, -1);
- close(syncFd);
- }
-
- return result;
+ return NO_ERROR;
}
status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
@@ -5954,16 +5871,14 @@
// as well. For now, just call directly to setActiveConfigWithConstraints but ideally
// it should go thru setDesiredActiveConfig, similar to primary display.
ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
- const auto displayId = display->getId();
- LOG_ALWAYS_FATAL_IF(!displayId);
+ const auto displayId = display->getPhysicalId();
hal::VsyncPeriodChangeConstraints constraints;
constraints.desiredTimeNanos = systemTime();
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
- if (getHwComposer().setActiveConfigWithConstraints(*displayId,
- policy->defaultConfig.value(),
+ if (getHwComposer().setActiveConfigWithConstraints(displayId, policy->defaultConfig.value(),
constraints, &timeline) < 0) {
return BAD_VALUE;
}
@@ -5973,11 +5888,9 @@
display->setActiveConfig(policy->defaultConfig);
const nsecs_t vsyncPeriod = getHwComposer()
- .getConfigs(*displayId)[policy->defaultConfig.value()]
+ .getConfigs(displayId)[policy->defaultConfig.value()]
->getVsyncPeriod();
- mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle,
- static_cast<PhysicalDisplayId>(
- *display->getId()),
+ mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId,
policy->defaultConfig, vsyncPeriod);
return NO_ERROR;
}
@@ -6009,8 +5922,8 @@
const nsecs_t vsyncPeriod =
mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
.getVsyncPeriod();
- mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
- static_cast<PhysicalDisplayId>(*display->getId()),
+ const auto physicalId = display->getPhysicalId();
+ mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
display->getActiveConfig(), vsyncPeriod);
toggleKernelIdleTimer();
@@ -6101,11 +6014,9 @@
} else if (display->isVirtual()) {
return INVALID_OPERATION;
} else {
- const auto displayId = display->getId();
- LOG_FATAL_IF(!displayId);
-
- *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
- auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+ const auto displayId = display->getPhysicalId();
+ *outDefaultConfig = getHwComposer().getActiveConfigIndex(displayId);
+ auto vsyncPeriod = getHwComposer().getActiveConfig(displayId)->getVsyncPeriod();
*outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
*outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
*outAppRequestRefreshRateMin = 1e9f / vsyncPeriod;
@@ -6184,9 +6095,6 @@
// on the work to remove the table in that bug rather than adding more to
// it.
static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
- // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially
- // supported, and exposed via the
- // IVrComposerClient::VrCommand::SET_LAYER_INFO command.
{"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
{"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
};
@@ -6203,6 +6111,11 @@
Mutex::Autolock lock(mStateLock);
if (authenticateSurfaceTextureLocked(surface)) {
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame rate on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
if (layer->setFrameRate(
Layer::FrameRate(frameRate,
Layer::FrameRate::convertCompatibility(compatibility)))) {
@@ -6286,6 +6199,24 @@
}));
}
+status_t SurfaceFlinger::setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+ int64_t frameTimelineVsyncId) {
+ Mutex::Autolock lock(mStateLock);
+ if (!authenticateSurfaceTextureLocked(surface)) {
+ ALOGE("Attempt to set frame timeline vsync on an unrecognized IGraphicBufferProducer");
+ return BAD_VALUE;
+ }
+
+ sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame timeline vsync on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
+ layer->setFrameTimelineVsyncForBuffer(frameTimelineVsyncId);
+ return NO_ERROR;
+}
+
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
static_cast<void>(schedule([=] {
std::unique_ptr<RefreshRateOverlay> overlay;
@@ -6309,6 +6240,17 @@
}));
}
+status_t SurfaceFlinger::addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+
+ mInterceptor->addTransactionTraceListener(listener);
+
+ return NO_ERROR;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 24837b8..f55dd90 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -52,6 +52,7 @@
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FrameTracker.h"
#include "LayerVector.h"
@@ -98,6 +99,10 @@
class TimeStats;
class FrameTracer;
+namespace frametimeline {
+class FrameTimeline;
+}
+
namespace os {
class IInputFlinger;
}
@@ -113,10 +118,6 @@
class RenderEngine;
} // namespace renderengine
-namespace dvr {
-class VrFlinger;
-} // namespace dvr
-
enum {
eTransactionNeeded = 0x01,
eTraversalNeeded = 0x02,
@@ -183,8 +184,15 @@
private HWC2::ComposerCallback,
private ISchedulerCallback {
public:
- SurfaceFlingerBE& getBE() { return mBE; }
- const SurfaceFlingerBE& getBE() const { return mBE; }
+ struct SkipInitializationTag {};
+
+ SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
+ explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
+
+ // set main thread scheduling policy
+ static status_t setSchedFifo(bool enabled) ANDROID_API;
+
+ static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
// This is the phase offset in nanoseconds of the software vsync event
// relative to the vsync event reported by HWComposer. The software vsync
@@ -212,7 +220,7 @@
// If fences from sync Framework are supported.
static bool hasSyncFramework;
- // The offset in nanoseconds to use when DispSync timestamps present fence
+ // The offset in nanoseconds to use when VsyncController timestamps present fence
// signaling time.
static int64_t dispSyncPresentTimeOffset;
@@ -263,17 +271,7 @@
// overhead that is caused by reading from sysprop.
static bool useFrameRateApi;
- // set main thread scheduling policy
- static status_t setSchedFifo(bool enabled) ANDROID_API;
-
- static char const* getServiceName() ANDROID_API {
- return "SurfaceFlinger";
- }
-
- struct SkipInitializationTag {};
static constexpr SkipInitializationTag SkipInitialization;
- SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
- explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
// must be called before clients can connect
void init() ANDROID_API;
@@ -281,6 +279,9 @@
// starts SurfaceFlinger main loop in the current thread
void run() ANDROID_API;
+ SurfaceFlingerBE& getBE() { return mBE; }
+ const SurfaceFlingerBE& getBE() const { return mBE; }
+
// Schedule an asynchronous or synchronous task on the main thread.
template <typename F, typename T = std::invoke_result_t<F>>
[[nodiscard]] std::future<T> schedule(F&&);
@@ -303,7 +304,7 @@
// called on the main thread by MessageQueue when an internal message
// is received
// TODO: this should be made accessible only to MessageQueue
- void onMessageReceived(int32_t what, nsecs_t expectedVSyncTime);
+ void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime);
renderengine::RenderEngine& getRenderEngine() const;
@@ -334,6 +335,24 @@
bool mDisableClientCompositionCache = false;
void setInputWindowsFinished();
+protected:
+ // We're reference counted, never destroy SurfaceFlinger directly
+ virtual ~SurfaceFlinger();
+
+ virtual uint32_t setClientStateLocked(
+ int64_t frameTimelineVsyncId, const ComposerState& composerState,
+ int64_t desiredPresentTime, int64_t postTime, bool privileged,
+ std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+ REQUIRES(mStateLock);
+ virtual void commitTransactionLocked();
+
+ // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
+ // root layers on a particular display in layer-coordinate space. The
+ // layers (and effectively their children) will be clipped against this
+ // rectangle. The base behavior is to clip to the visible region of the
+ // display.
+ virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
+
private:
friend class BufferLayer;
friend class BufferQueueLayer;
@@ -349,21 +368,18 @@
friend class TestableSurfaceFlinger;
friend class TransactionApplicationTest;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+ using VsyncModulator = scheduler::VsyncModulator;
+ using TransactionSchedule = scheduler::TransactionSchedule;
+ using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
+ using DumpArgs = Vector<String16>;
+ using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
+
// This value is specified in number of frames. Log frame stats at most
// every half hour.
enum { LOG_FRAME_STATS_PERIOD = 30*60*60 };
- static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
-
-protected:
- // We're reference counted, never destroy SurfaceFlinger directly
- virtual ~SurfaceFlinger();
-
-private:
- /* ------------------------------------------------------------------------
- * Internal data structures
- */
-
class State {
public:
explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {}
@@ -395,29 +411,119 @@
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
- /* ------------------------------------------------------------------------
- * IBinder interface
- */
+ struct ActiveConfigInfo {
+ HwcConfigIndexType configId;
+ Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
+
+ bool operator!=(const ActiveConfigInfo& other) const {
+ return configId != other.configId || event != other.event;
+ }
+ };
+
+ enum class BootStage {
+ BOOTLOADER,
+ BOOTANIMATION,
+ FINISHED,
+ };
+
+ struct HotplugEvent {
+ hal::HWDisplayId hwcDisplayId;
+ hal::Connection connection = hal::Connection::INVALID;
+ };
+
+ struct TransactionState {
+ TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+ int64_t postTime, bool privileged, bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
+ int originUid, uint64_t transactionId)
+ : frameTimelineVsyncId(frameTimelineVsyncId),
+ states(composerStates),
+ displays(displayStates),
+ flags(transactionFlags),
+ desiredPresentTime(desiredPresentTime),
+ buffer(uncacheBuffer),
+ postTime(postTime),
+ privileged(privileged),
+ hasListenerCallbacks(hasListenerCallbacks),
+ listenerCallbacks(listenerCallbacks),
+ originPid(originPid),
+ originUid(originUid),
+ id(transactionId) {}
+
+ int64_t frameTimelineVsyncId;
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags;
+ const int64_t desiredPresentTime;
+ client_cache_t buffer;
+ const int64_t postTime;
+ bool privileged;
+ bool hasListenerCallbacks;
+ std::vector<ListenerCallbacks> listenerCallbacks;
+ int originPid;
+ int originUid;
+ uint64_t id;
+ };
+
+ template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
+ static Dumper dumper(F&& dump) {
+ using namespace std::placeholders;
+ return std::bind(std::forward<F>(dump), _3);
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper dumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _3);
+ }
+
+ template <typename F>
+ Dumper argsDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _3);
+ }
+
+ template <typename F>
+ Dumper protoDumper(F dump) {
+ using namespace std::placeholders;
+ return std::bind(dump, this, _1, _2, _3);
+ }
+
+ template <typename... Args,
+ typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
+ void modulateVsync(Handler handler, Args... args) {
+ if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+ const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+ setVsyncConfig(*config, vsyncPeriod);
+ }
+ }
+
+ static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+ // Maximum allowed number of display frames that can be set through backdoor
+ static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
+
+ // Implements IBinder.
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
EXCLUDES(mStateLock);
- /* ------------------------------------------------------------------------
- * ISurfaceComposer interface
- */
+ // Implements ISurfaceComposer
sp<ISurfaceComposerClient> createConnection() override;
sp<IBinder> createDisplay(const String8& displayName, bool secure) override;
void destroyDisplay(const sp<IBinder>& displayToken) override;
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override;
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override;
- void setTransactionState(const Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks) override;
+ status_t setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
+ const Vector<DisplayState>& displays, uint32_t flags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+ bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) override;
void bootFinished() override;
bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -427,11 +533,11 @@
ISurfaceComposer::ConfigChanged configChanged =
ISurfaceComposer::eConfigChangedSuppress) override;
status_t captureDisplay(const DisplayCaptureArgs& args,
- ScreenCaptureResults& captureResults) override;
+ const sp<IScreenCaptureListener>& captureListener) override;
status_t captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults) override;
+ const sp<IScreenCaptureListener>& captureListener) override;
status_t captureLayers(const LayerCaptureArgs& args,
- ScreenCaptureResults& captureResults) override;
+ const sp<IScreenCaptureListener>& captureListener) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
@@ -495,17 +601,20 @@
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
- /* ------------------------------------------------------------------------
- * DeathRecipient interface
- */
+
+ status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
+ int64_t frameTimelineVsyncId) override;
+
+ status_t addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) override;
+
+ // Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
- /* ------------------------------------------------------------------------
- * RefBase interface
- */
+ // Implements RefBase.
void onFirstRef() override;
- /* ------------------------------------------------------------------------
+ /*
* HWC2::ComposerCallback / HWComposer::EventHandler interface
*/
void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId, int64_t timestamp,
@@ -518,7 +627,7 @@
const hal::VsyncPeriodChangeTimeline& updatedTimeline) override;
void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override;
- /* ------------------------------------------------------------------------
+ /*
* ISchedulerCallback
*/
@@ -540,7 +649,7 @@
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
- /* ------------------------------------------------------------------------
+ /*
* Message handling
*/
// Can only be called from the main thread or with mStateLock held
@@ -549,15 +658,6 @@
void signalLayerUpdate();
void signalRefresh();
- struct ActiveConfigInfo {
- HwcConfigIndexType configId;
- Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
-
- bool operator!=(const ActiveConfigInfo& other) const {
- return configId != other.configId || event != other.event;
- }
- };
-
// called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
@@ -583,7 +683,7 @@
// Handle the INVALIDATE message queue event, latching new buffers and applying
// incoming transactions
- void onMessageInvalidate(nsecs_t expectedVSyncTime);
+ void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -603,12 +703,9 @@
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
- using VsyncModulator = scheduler::VsyncModulator;
-
void initScheduler(PhysicalDisplayId primaryDisplayId);
void updatePhaseConfiguration(const RefreshRate&);
- void setVsyncConfig(const VsyncModulator::VsyncConfig&);
+ void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -616,20 +713,18 @@
*/
bool handlePageFlip();
- /* ------------------------------------------------------------------------
+ /*
* Transactions
*/
- using TransactionSchedule = scheduler::TransactionSchedule;
-
- void applyTransactionState(const Vector<ComposerState>& state,
+ void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime,
const client_cache_t& uncacheBuffer, const int64_t postTime,
bool privileged, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
- int originPID, int originUID, bool isMainThread = false)
- REQUIRES(mStateLock);
+ int originPid, int originUid, uint64_t transactionId,
+ bool isMainThread = false) REQUIRES(mStateLock);
// Returns true if at least one transaction was flushed
bool flushTransactionQueues();
// Returns true if there is at least one transaction that needs to be flushed
@@ -652,30 +747,14 @@
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
-
-protected:
- virtual uint32_t setClientStateLocked(
- const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
- bool privileged,
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
- REQUIRES(mStateLock);
- virtual void commitTransactionLocked();
-
- // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
- // root layers on a particular display in layer-coordinate space. The
- // layers (and effectively their children) will be clipped against this
- // rectangle. The base behavior is to clip to the visible region of the
- // display.
- virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
-
-private:
- /* ------------------------------------------------------------------------
+ /*
* Layer management
*/
status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags, LayerMetadata metadata,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+ const sp<IBinder>& parentHandle, int32_t* outLayerId,
+ const sp<Layer>& parentLayer = nullptr,
uint32_t* outTransformHint = nullptr);
status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
@@ -696,7 +775,7 @@
sp<IBinder>* outHandle, sp<Layer>* outLayer);
status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle);
+ sp<IBinder>* outHandle, int32_t* outLayerId);
std::string getUniqueLayerName(const char* name);
@@ -715,23 +794,17 @@
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
- /* ------------------------------------------------------------------------
- * Boot animation, on/off animations and screen capture
- */
-
+ // Boot animation, on/off animations and screen capture
void startBootAnim();
- using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
- using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>;
-
- status_t renderScreenImplLocked(const RenderArea& renderArea,
- TraverseLayersFunction traverseLayers,
- const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
- bool regionSampling, ScreenCaptureResults& captureResults);
+ status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
+ const sp<GraphicBuffer>&, bool forSystem, int* outSyncFd,
+ bool regionSampling, ScreenCaptureResults&);
status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
- ui::PixelFormat, ScreenCaptureResults& captureResults);
- status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&,
- bool regionSampling, ScreenCaptureResults& captureResults);
+ ui::PixelFormat, const sp<IScreenCaptureListener>&);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+ bool regionSampling, const sp<IScreenCaptureListener>&);
+
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
@@ -739,20 +812,12 @@
// matching ownerUid
void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
- sp<StartPropertySetThread> mStartPropertySetThread;
-
- /* ------------------------------------------------------------------------
- * Properties
- */
void readPersistentProperties();
- /* ------------------------------------------------------------------------
- * EGL
- */
size_t getMaxTextureSize() const;
size_t getMaxViewportDims() const;
- /* ------------------------------------------------------------------------
+ /*
* Display and layer stack management
*/
// called when starting, or restarting after system_server death
@@ -788,7 +853,7 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
- /* ------------------------------------------------------------------------
+ /*
* H/W composer
*/
@@ -797,8 +862,7 @@
// The following thread safety rules apply when accessing mHwc, either
// directly or via getHwComposer():
//
- // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc
- // only when switching into and out of vr. Recreating mHwc must only be
+ // 1. When recreating mHwc, acquire mStateLock. Recreating mHwc must only be
// done on the main thread.
//
// 2. When accessing mHwc on the main thread, it's not necessary to acquire
@@ -814,7 +878,7 @@
// acquiring mStateLock.
HWComposer& getHwComposer() const;
- /* ------------------------------------------------------------------------
+ /*
* Compositing
*/
void invalidateHwcGeometry();
@@ -828,7 +892,7 @@
void postFrame();
- /* ------------------------------------------------------------------------
+ /*
* Display management
*/
sp<DisplayDevice> setupNewDisplayDeviceInternal(
@@ -848,7 +912,7 @@
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
- /* ------------------------------------------------------------------------
+ /*
* VSYNC
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
@@ -888,8 +952,8 @@
return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
}
- std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
- REQUIRES(mStateLock) {
+ std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
+ const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
for (const auto& [id, token] : mPhysicalDisplayTokens) {
if (token == displayToken) {
return id;
@@ -912,33 +976,6 @@
/*
* Debugging & dumpsys
*/
- using DumpArgs = Vector<String16>;
- using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
-
- template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
- static Dumper dumper(F&& dump) {
- using namespace std::placeholders;
- return std::bind(std::forward<F>(dump), _3);
- }
-
- template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
- Dumper dumper(F dump) {
- using namespace std::placeholders;
- return std::bind(dump, this, _3);
- }
-
- template <typename F>
- Dumper argsDumper(F dump) {
- using namespace std::placeholders;
- return std::bind(dump, this, _1, _3);
- }
-
- template <typename F>
- Dumper protoDumper(F dump) {
- using namespace std::placeholders;
- return std::bind(dump, this, _1, _2, _3);
- }
-
void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
@@ -946,6 +983,7 @@
void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
void clearStatsLocked(const DumpArgs& args, std::string& result);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
+ void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats();
void dumpVSync(std::string& result) const REQUIRES(mStateLock);
@@ -982,20 +1020,28 @@
void onFrameRateFlexibilityTokenReleased();
- /* ------------------------------------------------------------------------
- * VrFlinger
- */
- void resetDisplayState() REQUIRES(mStateLock);
-
- // Check to see if we should handoff to vr flinger.
- void updateVrFlinger();
-
void updateColorMatrixLocked();
- /* ------------------------------------------------------------------------
- * Attributes
+ // Verify that transaction is being called by an approved process:
+ // either AID_GRAPHICS or AID_SYSTEM.
+ status_t CheckTransactCodeCredentials(uint32_t code);
+
+ /*
+ * Generic Layer Metadata
+ */
+ const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
+
+ /*
+ * Misc
*/
+ std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
+ return std::nullopt;
+ }
+
+ sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
// access must be protected by mStateLock
@@ -1042,7 +1088,11 @@
bool mInputInfoChanged = false;
bool mGeometryInvalid = false;
bool mAnimCompositionPending = false;
- std::vector<sp<Layer>> mLayersWithQueuedFrames;
+
+ // Tracks layers that have pending frames which are candidates for being
+ // latched. Because this contains a set of raw layer pointers, can only be
+ // mutated on the main thread.
+ std::unordered_set<Layer*> mLayersWithQueuedFrames;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE};
@@ -1057,17 +1107,8 @@
// did not change.
bool mReusedClientComposition = false;
- enum class BootStage {
- BOOTLOADER,
- BOOTANIMATION,
- FINISHED,
- };
BootStage mBootStage = BootStage::BOOTLOADER;
- struct HotplugEvent {
- hal::HWDisplayId hwcDisplayId;
- hal::Connection connection = hal::Connection::INVALID;
- };
std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
// this may only be written from the main thread with mStateLock held
@@ -1076,6 +1117,8 @@
std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
GUARDED_BY(mStateLock);
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
+
std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
// don't use a lock for these, we don't care
@@ -1085,7 +1128,7 @@
volatile nsecs_t mDebugInTransaction = 0;
bool mForceFullDamage = false;
bool mPropagateBackpressureClientComposition = false;
- std::unique_ptr<SurfaceInterceptor> mInterceptor;
+ sp<SurfaceInterceptor> mInterceptor;
SurfaceTracing mTracing{*this};
std::mutex mTracingLock;
@@ -1095,6 +1138,7 @@
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
+ const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline;
bool mUseHwcVirtualDisplays = false;
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
@@ -1130,40 +1174,9 @@
uint32_t mTexturePoolSize = 0;
std::vector<uint32_t> mTexturePool;
- struct TransactionState {
- TransactionState(const Vector<ComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- int64_t postTime, bool privileged, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> listenerCallbacks, int originPID,
- int originUID)
- : states(composerStates),
- displays(displayStates),
- flags(transactionFlags),
- desiredPresentTime(desiredPresentTime),
- buffer(uncacheBuffer),
- postTime(postTime),
- privileged(privileged),
- hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks),
- originPID(originPID),
- originUID(originUID) {}
-
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags;
- const int64_t desiredPresentTime;
- client_cache_t buffer;
- const int64_t postTime;
- bool privileged;
- bool hasListenerCallbacks;
- std::vector<ListenerCallbacks> listenerCallbacks;
- int originPID;
- int originUID;
- };
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
- /* ------------------------------------------------------------------------
+ /*
* Feature prototyping
*/
@@ -1172,10 +1185,6 @@
std::atomic<size_t> mNumLayers = 0;
- // Verify that transaction is being called by an approved process:
- // either AID_GRAPHICS or AID_SYSTEM.
- status_t CheckTransactCodeCredentials(uint32_t code);
-
// to linkToDeath
sp<IBinder> mWindowManager;
// We want to avoid multiple calls to BOOT_FINISHED as they come in on
@@ -1183,9 +1192,6 @@
// to mWindowManager or mInputFlinger
std::atomic<bool> mBootFinished = false;
- std::unique_ptr<dvr::VrFlinger> mVrFlinger;
- std::atomic<bool> mVrFlingerRequestsDisplay = false;
- static bool useVrFlinger;
std::thread::id mMainThreadId = std::this_thread::get_id();
DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced;
@@ -1206,7 +1212,7 @@
SurfaceFlingerBE mBE;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
- /* ------------------------------------------------------------------------
+ /*
* Scheduler
*/
std::unique_ptr<Scheduler> mScheduler;
@@ -1225,29 +1231,6 @@
std::atomic<nsecs_t> mExpectedPresentTime = 0;
hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
- template <typename... Args,
- typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
- void modulateVsync(Handler handler, Args... args) {
- if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- setVsyncConfig(*config);
- }
- }
-
- /* ------------------------------------------------------------------------
- * Generic Layer Metadata
- */
- const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
-
- /* ------------------------------------------------------------------------
- * Misc
- */
-
- std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
- return std::nullopt;
- }
-
std::mutex mActiveConfigLock;
// This bit is set once we start setting the config. We read from this bit during the
// process. If at the end, this bit is different than mDesiredActiveConfig, we restart
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 2e52155..9a8deae 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -37,10 +37,10 @@
#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/DispSync.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
namespace android::surfaceflinger {
@@ -68,9 +68,8 @@
return std::make_unique<Scheduler>(configs, callback);
}
-std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
- SurfaceFlinger* flinger) {
- return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
+ return new android::impl::SurfaceInterceptor();
}
sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 86e5a7a..40774ef 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -32,7 +32,7 @@
const scheduler::RefreshRateConfigs&) override;
std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
- std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) 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,
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index a0dd999..2dd563b 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -34,7 +34,6 @@
class EffectLayer;
class ContainerLayer;
class DisplayDevice;
-class DispSync;
class GraphicBuffer;
class HWComposer;
class IGraphicBufferConsumer;
@@ -56,6 +55,7 @@
namespace scheduler {
class VsyncConfiguration;
+class VsyncController;
class RefreshRateConfigs;
} // namespace scheduler
@@ -73,7 +73,7 @@
const scheduler::RefreshRateConfigs&) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
- virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
+ virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
bool timestampPropertyValue) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index c15d0df..da58d4e 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -40,9 +40,22 @@
namespace impl {
-SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger)
- : mFlinger(flinger)
-{
+void SurfaceInterceptor::addTransactionTraceListener(
+ const sp<gui::ITransactionTraceListener>& listener) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+ std::scoped_lock lock(mListenersMutex);
+
+ asBinder->linkToDeath(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,
@@ -52,8 +65,14 @@
return;
}
ATRACE_CALL();
+ {
+ std::scoped_lock lock(mListenersMutex);
+ for (const auto& [_, listener] : mTraceToggledListeners) {
+ listener->onToggled(true);
+ }
+ }
mEnabled = true;
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
saveExistingDisplaysLocked(displays);
saveExistingSurfacesLocked(layers);
}
@@ -63,8 +82,14 @@
return;
}
ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ {
+ 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");
@@ -118,7 +143,7 @@
if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
addDeferTransactionLocked(transaction, layerId,
layer->mCurrentState.barrierLayer_legacy.promote(),
- layer->mCurrentState.frameNumber_legacy);
+ layer->mCurrentState.barrierFrameNumber);
}
addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
@@ -442,34 +467,34 @@
}
if (state.what & layer_state_t::eDeferTransaction_legacy) {
sp<Layer> otherLayer = nullptr;
- if (state.barrierHandle_legacy != nullptr) {
- otherLayer =
- static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote();
- } else if (state.barrierGbp_legacy != nullptr) {
- auto const& gbp = state.barrierGbp_legacy;
- if (mFlinger->authenticateSurfaceTextureLocked(gbp)) {
- otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
- } else {
- ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer");
- }
+ if (state.barrierSurfaceControl_legacy != nullptr) {
+ otherLayer = static_cast<Layer::Handle*>(
+ state.barrierSurfaceControl_legacy->getHandle().get())
+ ->owner.promote();
}
- addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy);
+ addDeferTransactionLocked(transaction, layerId, otherLayer, state.barrierFrameNumber);
}
if (state.what & layer_state_t::eOverrideScalingModeChanged) {
addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
}
if (state.what & layer_state_t::eReparent) {
- addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild));
+ auto parentHandle = (state.parentSurfaceControlForChild)
+ ? state.parentSurfaceControlForChild->getHandle()
+ : nullptr;
+ addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
}
if (state.what & layer_state_t::eReparentChildren) {
- addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle));
+ addReparentChildrenLocked(transaction, layerId,
+ getLayerIdFromHandle(state.reparentSurfaceControl->getHandle()));
}
if (state.what & layer_state_t::eDetachChildren) {
addDetachChildrenLocked(transaction, layerId, true);
}
if (state.what & layer_state_t::eRelativeLayerChanged) {
addRelativeParentLocked(transaction, layerId,
- getLayerIdFromHandle(state.relativeLayerHandle), state.z);
+ getLayerIdFromHandle(
+ state.relativeLayerSurfaceControl->getHandle()),
+ state.z);
}
if (state.what & layer_state_t::eShadowRadiusChanged) {
addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
@@ -497,13 +522,13 @@
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) {
+ 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);
-
+ setTransactionOriginLocked(transaction, originPid, originUid);
+ transaction->set_id(transactionId);
for (const auto& compState: stateUpdates) {
addSurfaceChangesLocked(transaction, compState.state);
}
@@ -625,14 +650,15 @@
void SurfaceInterceptor::saveTransaction(
const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
- const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPID, int originUID) {
+ 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);
+ flags, originPid, originUid, transactionId);
}
void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 1798b5a..9ac189a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -21,6 +21,8 @@
#include <mutex>
+#include <binder/IBinder.h>
+
#include <gui/LayerState.h>
#include <utils/KeyedVector.h>
@@ -48,7 +50,7 @@
constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
-class SurfaceInterceptor {
+class SurfaceInterceptor : public IBinder::DeathRecipient {
public:
virtual ~SurfaceInterceptor();
@@ -58,12 +60,16 @@
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) = 0;
+ 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;
@@ -86,7 +92,7 @@
*/
class SurfaceInterceptor final : public android::SurfaceInterceptor {
public:
- explicit SurfaceInterceptor(SurfaceFlinger* const flinger);
+ SurfaceInterceptor() = default;
~SurfaceInterceptor() override = default;
// Both vectors are used to capture the current state of SF as the initial snapshot in the trace
@@ -95,11 +101,14 @@
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) override;
+ 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;
@@ -164,7 +173,8 @@
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);
+ uint32_t transactionFlags, int originPid, int originUid,
+ uint64_t transactionId);
void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
@@ -192,7 +202,9 @@
std::string mOutputFileName {DEFAULT_FILENAME};
std::mutex mTraceMutex {};
Trace mTrace {};
- SurfaceFlinger* const mFlinger;
+ std::mutex mListenersMutex;
+ std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
+ GUARDED_BY(mListenersMutex);
};
} // namespace impl
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 894ee6d..d77387a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -112,7 +112,7 @@
StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
StringAppendF(&result, "displayConfigStats is as below:\n");
for (const auto& [fps, duration] : refreshRateStats) {
- StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+ StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
}
result.back() = '\n';
StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 4e7f67d..49cf80c 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -21,12 +21,32 @@
#include <cmath>
#include <string>
+namespace std {
+template <class Rep, class Period>
+bool signbit(std::chrono::duration<Rep, Period> v) {
+ return signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
+}
+} // namespace std
+
namespace android {
+namespace {
+template <typename T>
+int64_t to_int64(T v) {
+ return int64_t(v);
+}
+
+template <class Rep, class Period>
+int64_t to_int64(std::chrono::duration<Rep, Period> v) {
+ return int64_t(v.count());
+}
+} // namespace
+
template <typename T>
class TracedOrdinal {
public:
- static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()),
+ static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) ||
+ std::is_same<std::chrono::nanoseconds, T>(),
"Type is not supported. Please test it with systrace before adding "
"it to the list.");
@@ -57,12 +77,12 @@
}
if (!std::signbit(mData)) {
- ATRACE_INT64(mName.c_str(), int64_t(mData));
+ ATRACE_INT64(mName.c_str(), to_int64(mData));
if (mHasGoneNegative) {
ATRACE_INT64(mNameNegative.c_str(), 0);
}
} else {
- ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData));
+ ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData));
ATRACE_INT64(mName.c_str(), 0);
}
}
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index d03cb7b..0a73b23 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -1,5 +1,5 @@
-cc_library_shared {
- name: "liblayers_proto",
+cc_defaults {
+ name: "liblayers_proto_defaults",
export_include_dirs: ["include"],
srcs: [
@@ -19,7 +19,7 @@
proto: {
export_proto_headers: true,
},
-
+
cppflags: [
"-Werror",
"-Wno-unused-parameter",
@@ -33,7 +33,20 @@
"-Wno-old-style-cast",
"-Wno-undef",
],
+}
+cc_library_shared {
+ name: "liblayers_proto",
+ defaults: [
+ "liblayers_proto_defaults",
+ ],
+}
+
+cc_library_static {
+ name: "liblayers_proto_static",
+ defaults: [
+ "liblayers_proto_defaults",
+ ],
}
java_library_static {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 7b1f0fb..7666f7f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -72,7 +72,7 @@
prop {
api_name: "max_graphics_width"
type: Integer
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.max_graphics_width"
}
@@ -82,7 +82,7 @@
prop {
api_name: "max_graphics_height"
type: Integer
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.max_graphics_height"
}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index b66e56e..ba60a7d 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -36,6 +36,11 @@
prop_name: "ro.surface_flinger.display_primary_white"
}
prop {
+ api_name: "display_update_imminent_timeout_ms"
+ type: Integer
+ prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
+ }
+ prop {
api_name: "enable_protected_contents"
prop_name: "ro.surface_flinger.protected_contents"
}
@@ -57,6 +62,16 @@
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
}
prop {
+ api_name: "max_graphics_height"
+ type: Integer
+ prop_name: "ro.surface_flinger.max_graphics_height"
+ }
+ prop {
+ api_name: "max_graphics_width"
+ type: Integer
+ prop_name: "ro.surface_flinger.max_graphics_width"
+ }
+ prop {
api_name: "max_virtual_display_dimension"
type: Long
prop_name: "ro.surface_flinger.max_virtual_display_dimension"
@@ -73,6 +88,11 @@
enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
}
prop {
+ api_name: "refresh_rate_switching"
+ prop_name: "ro.surface_flinger.refresh_rate_switching"
+ deprecated: true
+ }
+ prop {
api_name: "running_without_sync_framework"
prop_name: "ro.surface_flinger.running_without_sync_framework"
}
@@ -100,16 +120,29 @@
prop_name: "ro.surface_flinger.support_kernel_idle_timer"
}
prop {
+ api_name: "supports_background_blur"
+ prop_name: "ro.surface_flinger.supports_background_blur"
+ }
+ prop {
api_name: "use_color_management"
prop_name: "ro.surface_flinger.use_color_management"
}
prop {
+ api_name: "use_content_detection_for_refresh_rate"
+ prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+ }
+ prop {
api_name: "use_context_priority"
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
+ api_name: "use_frame_rate_api"
+ prop_name: "ro.surface_flinger.use_frame_rate_api"
+ }
+ prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
+ deprecated: true
}
prop {
api_name: "use_vr_flinger"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 719c46e..db5d820 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -34,6 +34,7 @@
"LayerUpdate_test.cpp",
"MirrorLayer_test.cpp",
"MultiDisplayLayerBounds_test.cpp",
+ "RefreshRateOverlay_test.cpp",
"RelativeZ_test.cpp",
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
@@ -45,18 +46,19 @@
data: ["SurfaceFlinger_test.filter"],
static_libs: [
"libtrace_proto",
+ "liblayers_proto_static",
+ "android.hardware.graphics.composer@2.1",
],
shared_libs: [
"android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.composer@2.1",
"libandroid",
+ "libbase",
"libbinder",
"libcutils",
"libEGL",
"libGLESv2",
"libgui",
- "liblayers_proto",
"liblog",
"libnativewindow",
"libprotobuf-cpp-full",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 10a517e..7f541e2 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,3 +1,23 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
@@ -7,8 +27,8 @@
#include <private/gui/ComposerService.h>
#include <ui/DisplayConfig.h>
#include <utils/String8.h>
-
#include <functional>
+#include "utils/ScreenshotUtils.h"
namespace android {
@@ -78,26 +98,6 @@
t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
}
- void setupVirtualDisplay() {
- mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
- const ssize_t displayWidth = 100;
- const ssize_t displayHeight = 100;
-
- // Background surface
- mVirtualSurfaceControl =
- mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mVirtualSurfaceControl != nullptr);
- ASSERT_TRUE(mVirtualSurfaceControl->isValid());
-
- Transaction t;
- t.setDisplayLayerStack(mVirtualDisplay, 0);
- ASSERT_EQ(NO_ERROR,
- t.setLayer(mVirtualSurfaceControl, INT_MAX - 3)
- .show(mVirtualSurfaceControl)
- .apply());
- }
-
/**
* Sets UID to imitate Graphic's process.
*/
@@ -145,6 +145,10 @@
// Check as a non-supported user.
setBinUID();
ASSERT_EQ(unprivilegedValue, condition());
+
+ // Check as shell since shell has some additional permissions
+ seteuid(AID_SHELL);
+ ASSERT_EQ(unprivilegedValue, condition());
}
};
@@ -187,7 +191,7 @@
Vector<DisplayConfig> configs;
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
+ ASSERT_TRUE(SurfaceComposerClient::getActiveConfig(display) >= 0);
ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE),
SurfaceComposerClient::getActiveColorMode(display));
@@ -242,11 +246,31 @@
}
TEST_F(CredentialsTest, CreateDisplayTest) {
+ // Only graphics and system processes can create a secure display.
std::function<bool()> condition = [=]() {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
return testDisplay.get() != nullptr;
};
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
+
+ // Check with root.
+ seteuid(AID_ROOT);
+ ASSERT_FALSE(condition());
+
+ // Check as a Graphics user.
+ setGraphicsUID();
+ ASSERT_TRUE(condition());
+
+ // Check as a system user.
+ setSystemUID();
+ ASSERT_TRUE(condition());
+
+ // Check as a non-supported user.
+ setBinUID();
+ ASSERT_FALSE(condition());
+
+ // Check as shell since shell has some additional permissions
+ seteuid(AID_SHELL);
+ ASSERT_FALSE(condition());
condition = [=]() {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
@@ -262,7 +286,7 @@
DisplayCaptureArgs captureArgs;
captureArgs.displayToken = display;
ScreenCaptureResults captureResults;
- return ScreenshotClient::captureDisplay(captureArgs, captureResults);
+ return ScreenCapture::captureDisplay(captureArgs, captureResults);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
@@ -276,7 +300,7 @@
captureArgs.sourceCrop = {0, 0, 1, 1};
ScreenCaptureResults captureResults;
- return ScreenshotClient::captureLayers(captureArgs, captureResults);
+ return ScreenCapture::captureLayers(captureArgs, captureResults);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
diff --git a/services/surfaceflinger/tests/DetachChildren_test.cpp b/services/surfaceflinger/tests/DetachChildren_test.cpp
index b6c2fe2..9c7b1fc 100644
--- a/services/surfaceflinger/tests/DetachChildren_test.cpp
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -61,7 +61,7 @@
TransactionUtils::fillSurfaceRGBA8(relative, relativeColor);
Transaction{}
- .setRelativeLayer(relative, mMainSurface->getHandle(), 1)
+ .setRelativeLayer(relative, mMainSurface, 1)
.setPosition(relative, relBounds.left, relBounds.top)
.apply();
@@ -204,7 +204,7 @@
.setLayer(newParentSurface, INT32_MAX - 1)
.show(newParentSurface)
.setPosition(newParentSurface, newParentBounds.left, newParentBounds.top)
- .reparent(childNewClient, newParentSurface->getHandle())
+ .reparent(childNewClient, newParentSurface)
.apply();
{
mCapture = screenshot();
@@ -238,7 +238,7 @@
}
Transaction()
- .deferTransactionUntil_legacy(childNewClient, mMainSurface->getHandle(),
+ .deferTransactionUntil_legacy(childNewClient, mMainSurface,
mMainSurface->getSurface()->getNextFrameNumber())
.apply();
Transaction().detachChildren(mMainSurface).apply();
@@ -256,6 +256,58 @@
}
}
+/**
+ * Tests that a deferring transaction on an already detached layer will be dropped gracefully and
+ * allow the barrier layer to dequeue buffers.
+ *
+ * Fixes b/150924737 - buffer cannot be latched because it waits for a detached layer
+ * to commit its pending states.
+ */
+TEST_F(DetachChildren, DeferredTransactionOnDetachedChildren) {
+ Color childColor = {200, 200, 200, 255};
+ Rect childBounds = Rect(74, 74, 84, 84);
+
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+ childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+ ASSERT_TRUE(childNewClient->isValid());
+
+ TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+ Transaction()
+ .show(childNewClient)
+ .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+ childBounds.top - mMainSurfaceBounds.top)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, mMainSurfaceColor);
+ mCapture->expectColor(childBounds, childColor);
+ }
+
+ Transaction().detachChildren(mMainSurface).apply();
+ Transaction()
+ .setCrop_legacy(childNewClient, {0, 0, childBounds.width(), childBounds.height()})
+ .deferTransactionUntil_legacy(childNewClient, mMainSurface,
+ mMainSurface->getSurface()->getNextFrameNumber())
+ .apply();
+
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED,
+ mMainSurfaceBounds.width(),
+ mMainSurfaceBounds.height()));
+
+ // BufferLayer can still dequeue buffers even though there's a detached layer with a
+ // deferred transaction.
+ {
+ SCOPED_TRACE("new buffer");
+ mCapture = screenshot();
+ mCapture->expectBorder(childBounds, Color::RED);
+ mCapture->expectColor(childBounds, childColor);
+ }
+}
+
TEST_F(DetachChildren, ReparentParentLayerOfDetachedChildren) {
Color childColor = {200, 200, 200, 255};
Rect childBounds = Rect(74, 74, 94, 94);
@@ -300,7 +352,7 @@
mCapture->expectColor(mMainSurfaceBounds, Color::BLACK);
}
- Transaction().reparent(mMainSurface, mBlackBgSurface->getHandle()).apply();
+ Transaction().reparent(mMainSurface, mBlackBgSurface).apply();
{
mCapture = screenshot();
mCapture->expectBorder(childBounds, mMainSurfaceColor);
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 300e9c7..cfec0d2 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -14,6 +14,10 @@
* 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 <binder/Binder.h>
#include <gtest/gtest.h>
@@ -22,6 +26,7 @@
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
#include <ui/Rect.h>
+#include "utils/ScreenshotUtils.h"
namespace android {
namespace {
@@ -56,13 +61,11 @@
}
TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-
LayerCaptureArgs args;
args.layerHandle = mNotSc->getHandle();
ScreenCaptureResults captureResults;
- ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}
} // namespace
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 83e5060..4947289 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -211,16 +211,13 @@
switch (layerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
+ Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
Transaction()
.setFrame(layerR, Rect(0, 0, 32, 32))
.setFrame(layerG, Rect(16, 16, 48, 48))
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
+ .setRelativeLayer(layerG, layerR, 1)
.apply();
break;
default:
@@ -233,7 +230,7 @@
shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
}
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
+ Transaction().setRelativeLayer(layerG, layerR, -1).apply();
{
SCOPED_TRACE("layerG below");
auto shot = getScreenCapture();
@@ -266,7 +263,7 @@
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
Transaction()
.setPosition(layerG, 8, 8)
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setRelativeLayer(layerG, layerR, 3)
.setPosition(layerB, 16, 16)
.setLayer(layerB, mLayerZBase + 2)
.apply();
@@ -275,7 +272,7 @@
Transaction()
.setFrame(layerR, Rect(0, 0, 32, 32))
.setFrame(layerG, Rect(8, 8, 40, 40))
- .setRelativeLayer(layerG, layerR->getHandle(), 3)
+ .setRelativeLayer(layerG, layerR, 3)
.setFrame(layerB, Rect(16, 16, 48, 48))
.setLayer(layerB, mLayerZBase + 2)
.apply();
@@ -303,7 +300,7 @@
}
// layerR = 4, layerG = layerR - 3, layerB = 2
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
+ Transaction().setRelativeLayer(layerG, layerR, -3).apply();
{
SCOPED_TRACE("layerB < (layerG < layerR)");
auto shot = getScreenCapture();
@@ -810,7 +807,7 @@
// channel) should be less than one
const uint8_t tolerance = 1;
Transaction()
- .reparent(colorLayer, parentLayer->getHandle())
+ .reparent(colorLayer, parentLayer)
.setColor(colorLayer, color)
.setAlpha(parentLayer, alpha)
.setLayer(parentLayer, mLayerZBase + 1)
@@ -1225,7 +1222,7 @@
child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().reparent(child, parent->getHandle()).apply();
+ Transaction().reparent(child, parent).apply();
// A layer will default to the frame of its parent
auto shot = getScreenCapture();
@@ -1242,7 +1239,7 @@
child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
- Transaction().reparent(child, parent->getHandle()).apply();
+ Transaction().reparent(child, parent).apply();
// A layer will default to the frame of its parent
auto shot = getScreenCapture();
@@ -1272,7 +1269,7 @@
parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
ASSERT_NO_FATAL_FAILURE(
child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
- Transaction().reparent(child, parent->getHandle()).apply();
+ Transaction().reparent(child, parent).apply();
ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 785c2c3..e66df4a 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -83,6 +83,7 @@
results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0);
results.capturedSecureLayers = true;
results.capturedDataspace = ui::Dataspace::DISPLAY_P3;
+ results.result = BAD_VALUE;
Parcel p;
results.write(p);
@@ -98,6 +99,7 @@
ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat());
ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers);
ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace);
+ ASSERT_EQ(results.result, results2.result);
}
} // namespace test
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index 8d715e1..ef992d6 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -50,7 +50,7 @@
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- Transaction().reparent(layer, layer->getHandle()).apply();
+ Transaction().reparent(layer, layer).apply();
{
// We expect the transaction to be silently dropped, but for SurfaceFlinger
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 7d4314f..c57ad43 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -87,10 +87,7 @@
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
- Transaction()
- .setPosition(layerG, 16, 16)
- .setRelativeLayer(layerG, layerR->getHandle(), 1)
- .apply();
+ Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
Transaction().reparent(layerG, nullptr).apply();
@@ -154,10 +151,7 @@
ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
- Transaction()
- .reparent(layerR, parent->getHandle())
- .reparent(layerG, parent->getHandle())
- .apply();
+ Transaction().reparent(layerR, parent).reparent(layerG, parent).apply();
Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
{
SCOPED_TRACE("layerR");
@@ -241,7 +235,7 @@
auto transaction = Transaction()
.setCornerRadius(parent, cornerRadius)
.setCrop_legacy(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
+ .reparent(child, parent)
.setPosition(child, 0, size)
// Rotate by half PI
.setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
@@ -283,14 +277,14 @@
Transaction()
.setCornerRadius(parent, cornerRadius)
.setCrop_legacy(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
+ .reparent(child, parent)
.setPosition(child, 0, size / 2)
.apply();
} else {
Transaction()
.setCornerRadius(parent, cornerRadius)
.setFrame(parent, Rect(0, 0, size, size))
- .reparent(child, parent->getHandle())
+ .reparent(child, parent)
.setFrame(child, Rect(0, size / 2, size, size))
.apply();
}
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index e3b9489..f8a0bc1 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -54,10 +54,10 @@
ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
- Transaction().reparent(layerB, parent->getHandle()).apply();
+ Transaction().reparent(layerB, parent).apply();
// layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
- Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
+ Transaction().setRelativeLayer(layerG, layerR, -1).setLayer(layerB, -2).apply();
std::unique_ptr<ScreenCapture> screenshot;
// only layerB is in this range
@@ -88,10 +88,7 @@
.setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
.apply();
- Transaction()
- .setRelativeLayer(childLayer, parent->getHandle(), -1)
- .setLayer(childLayer, 1)
- .apply();
+ Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply();
{
SCOPED_TRACE("setLayer above");
@@ -101,10 +98,7 @@
screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
}
- Transaction()
- .setLayer(childLayer, 1)
- .setRelativeLayer(childLayer, parent->getHandle(), -1)
- .apply();
+ Transaction().setLayer(childLayer, 1).setRelativeLayer(childLayer, parent, -1).apply();
{
SCOPED_TRACE("setRelative below");
@@ -141,7 +135,7 @@
.setLayer(relativeParent, mLayerZBase)
.apply();
- Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply();
+ Transaction().setRelativeLayer(childLayer, relativeParent, 1).apply();
{
SCOPED_TRACE("setLayer above");
@@ -167,7 +161,6 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
sp<GraphicBuffer> outBuffer;
Transaction()
.setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
@@ -177,11 +170,12 @@
args.displayToken = mDisplay;
ScreenCaptureResults captureResults;
- ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(args, captureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults));
Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
- ASSERT_EQ(NO_ERROR, composer->captureDisplay(args, captureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults));
}
+
TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index c56d473..38da0b1 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -173,13 +173,13 @@
// set up two deferred transactions on different frames
asTransaction([&](Transaction& t) {
t.setAlpha(mFGSurfaceControl, 0.75);
- t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+ t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl,
mSyncSurfaceControl->getSurface()->getNextFrameNumber());
});
asTransaction([&](Transaction& t) {
t.setPosition(mFGSurfaceControl, 128, 128);
- t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+ t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl,
mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
});
@@ -480,9 +480,8 @@
mCapture->expectFGColor(84, 84);
}
- asTransaction([&](Transaction& t) {
- t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
- });
+ asTransaction(
+ [&](Transaction& t) { t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl); });
{
mCapture = screenshot();
@@ -516,7 +515,7 @@
mCapture->expectFGColor(64, 64);
}
- asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); });
+ asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl); });
{
SCOPED_TRACE("After reparenting grandchild");
@@ -531,9 +530,7 @@
TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
// draw grand child behind the foreground surface
- asTransaction([&](Transaction& t) {
- t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
- });
+ asTransaction([&](Transaction& t) { t.setRelativeLayer(mGrandChild, mFGSurfaceControl, -1); });
{
SCOPED_TRACE("Child visible");
@@ -543,7 +540,7 @@
asTransaction([&](Transaction& t) {
t.reparent(mChild, nullptr);
- t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
+ t.reparentChildren(mChild, mFGSurfaceControl);
});
{
@@ -739,7 +736,7 @@
// Show the child layer in a deferred transaction
asTransaction([&](Transaction& t) {
- t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+ t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl,
mFGSurfaceControl->getSurface()->getNextFrameNumber());
t.show(mChild);
});
@@ -776,7 +773,7 @@
mCapture->expectFGColor(84, 84);
}
- asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
+ asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl); });
{
mCapture = screenshot();
@@ -838,7 +835,7 @@
mCapture->checkPixel(10, 10, 63, 195, 63);
}
- asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
+ asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl); });
{
mCapture = screenshot();
@@ -869,7 +866,7 @@
Transaction t;
t.setLayer(relative, INT32_MAX)
- .setRelativeLayer(mChild, relative->getHandle(), 1)
+ .setRelativeLayer(mChild, relative, 1)
.setPosition(mFGSurfaceControl, 0, 0)
.apply(true);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index b49bd54..16826c1 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -68,7 +68,7 @@
// Add mirrorLayer as child of mParentLayer so it's shown on the display
Transaction()
- .reparent(mirrorLayer, mParentLayer->getHandle())
+ .reparent(mirrorLayer, mParentLayer)
.setPosition(mirrorLayer, 500, 500)
.show(mirrorLayer)
.apply();
@@ -127,7 +127,7 @@
}
// Add grandchild layer to offscreen layer
- Transaction().reparent(grandchild, mChildLayer->getHandle()).apply();
+ Transaction().reparent(grandchild, mChildLayer).apply();
{
SCOPED_TRACE("Added Grandchild Layer");
auto shot = screenshot();
@@ -138,7 +138,7 @@
}
// Add child layer
- Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply();
+ Transaction().reparent(mChildLayer, mParentLayer).apply();
{
SCOPED_TRACE("Added Child Layer");
auto shot = screenshot();
@@ -157,7 +157,7 @@
sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
Transaction()
- .reparent(mirrorLayer, mParentLayer->getHandle())
+ .reparent(mirrorLayer, mParentLayer)
.setPosition(mirrorLayer, 500, 500)
.show(mirrorLayer)
.apply();
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
new file mode 100644
index 0000000..05858bf
--- /dev/null
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+
+static constexpr int kRefreshRateOverlayCode = 1034;
+static constexpr int kRefreshRateOverlayEnable = 1;
+static constexpr int kRefreshRateOverlayDisable = 0;
+static constexpr int kRefreshRateOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowRefreshRatePreferenceController
+static_assert(kRefreshRateOverlayCode == 1034);
+static_assert(kRefreshRateOverlayEnable == 1);
+static_assert(kRefreshRateOverlayDisable == 0);
+static_assert(kRefreshRateOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ Parcel request;
+ request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ request.writeInt32(command);
+ ASSERT_EQ(NO_ERROR,
+ IInterface::asBinder(sf)->transact(kRefreshRateOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+ Parcel reply;
+ sendCommandToSf(kRefreshRateOverlayQuery, reply);
+ return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+ static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+ static constexpr auto kIterations = 10;
+ for (int i = 0; i < kIterations; i++) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+ std::this_thread::sleep_for(kTimeout / kIterations);
+ }
+}
+
+void toggleOverlay(bool enabled) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+
+ Parcel reply;
+ const auto command = enabled ? kRefreshRateOverlayEnable : kRefreshRateOverlayDisable;
+ sendCommandToSf(command, reply);
+ waitForOverlay(enabled);
+ ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(RefreshRateOverlayTest, enableOverlay) {
+ toggleOverlay(true);
+}
+
+TEST(RefreshRateOverlayTest, disableOverlay) {
+ toggleOverlay(false);
+}
+
+TEST(RefreshRateOverlayTest, enableAndDisableOverlay) {
+ toggleOverlay(true);
+ toggleOverlay(false);
+
+ toggleOverlay(true);
+ toggleOverlay(false);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 3e0b3c6..fde6e6e 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -70,10 +70,7 @@
sp<SurfaceControl> childLayer =
createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
- Transaction{}
- .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
- .show(childLayer)
- .apply();
+ Transaction{}.setRelativeLayer(childLayer, mForegroundLayer, 1).show(childLayer).apply();
{
// The childLayer should be in front of the FG control.
@@ -88,7 +85,7 @@
// Background layer (RED)
// Child layer (WHITE)
// Foregroud layer (GREEN)
- Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLayer, mBackgroundLayer).apply();
{
// The relative z info for child layer should be reset, leaving FG control on top.
@@ -118,7 +115,7 @@
createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
Transaction{}
- .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+ .setRelativeLayer(childLevel3, childLevel2b, 1)
.show(childLevel2a)
.show(childLevel2b)
.show(childLevel3)
@@ -140,7 +137,7 @@
// child level 2 back (BLUE)
// child level 3 (GREEN) (relative to child level 2b)
// child level 2 front (BLACK)
- Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLevel1, mForegroundLayer).apply();
{
// Nothing should change at this point since relative z info was preserved.
@@ -162,7 +159,7 @@
createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get());
Transaction{}
- .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1)
+ .setRelativeLayer(childLayer, relativeToLayer, 1)
.show(childLayer)
.show(relativeToLayer)
.apply();
@@ -199,7 +196,7 @@
// Background layer (RED)
// Foregroud layer (GREEN)
// Child layer (BLUE)
- Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLayer, mForegroundLayer).apply();
{
// The relative z info for child layer should be reset, leaving the child layer on top.
@@ -230,7 +227,7 @@
createColorLayer("child level 2b", Color::BLACK, childLevel1b.get());
Transaction{}
- .setRelativeLayer(childLevel1a, childLevel2b->getHandle(), 1)
+ .setRelativeLayer(childLevel1a, childLevel2b, 1)
.show(childLevel1a)
.show(childLevel1b)
.show(childLevel2a)
@@ -250,7 +247,7 @@
// // Background layer (RED)
// // Foregroud layer (GREEN)
- Transaction{}.reparent(childLevel1a, childLevel2a->getHandle()).apply();
+ Transaction{}.reparent(childLevel1a, childLevel2a).apply();
{
// The childLevel1a and childLevel1b are no longer on screen
@@ -264,7 +261,7 @@
// child level 2a (BLUE)
// child level 1a (testLayerColor) (relative to child level 2b)
// child level 2b (BLACK)
- Transaction{}.reparent(childLevel1b, mForegroundLayer->getHandle()).apply();
+ Transaction{}.reparent(childLevel1b, mForegroundLayer).apply();
{
// Nothing should change at this point since relative z info was preserved.
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 690f758..7df3711 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -84,14 +84,13 @@
Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
UIDFaker f(AID_SYSTEM);
// By default the system can capture screenshots with secure layers but they
// will be blacked out
- ASSERT_EQ(NO_ERROR, composer->captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
{
SCOPED_TRACE("as system");
@@ -104,12 +103,47 @@
DisplayCaptureArgs args;
args.displayToken = mDisplay;
args.captureSecureLayers = true;
- ASSERT_EQ(NO_ERROR, composer->captureDisplay(args, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer);
sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
}
+TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) {
+ sp<SurfaceControl> parentLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ parentLayer = createLayer("parent-test", 32, 32,
+ ISurfaceComposerClient::eSecure |
+ ISurfaceComposerClient::eFXSurfaceBufferQueue));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
+
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("child-test", 10, 10,
+ ISurfaceComposerClient::eFXSurfaceBufferQueue,
+ parentLayer.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(childLayer, Color::BLUE, 10, 10));
+
+ Transaction().show(parentLayer).setLayer(parentLayer, INT32_MAX).show(childLayer).apply(true);
+
+ UIDFaker f(AID_SYSTEM);
+
+ {
+ SCOPED_TRACE("as system");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 10, 10), Color::BLACK);
+ }
+
+ // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+ // to receive them...we are expected to take care with the results.
+ DisplayCaptureArgs args;
+ args.displayToken = mDisplay;
+ args.captureSecureLayers = true;
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+ ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
+ ScreenCapture sc(mCaptureResults.buffer);
+ sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
+}
+
TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mBGSurfaceControl->getHandle();
@@ -240,7 +274,7 @@
SurfaceComposerClient::Transaction()
.show(child)
// Set relative layer above fg layer so should be shown above when computing all layers.
- .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
+ .setRelativeLayer(relative, mFGSurfaceControl, 1)
.show(relative)
.apply(true);
@@ -265,7 +299,7 @@
// Set relative layer below fg layer but relative to child layer so it should be shown
// above child layer.
.setLayer(relative, -1)
- .setRelativeLayer(relative, child->getHandle(), 1)
+ .setRelativeLayer(relative, child, 1)
.show(relative)
.apply(true);
@@ -307,21 +341,17 @@
sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
SurfaceComposerClient::Transaction().show(child).apply(true);
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-
LayerCaptureArgs args;
args.layerHandle = child->getHandle();
ScreenCaptureResults captureResults;
- ASSERT_EQ(BAD_VALUE, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
}
TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
SurfaceComposerClient::Transaction().show(child).apply(true);
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sp<GraphicBuffer> outBuffer;
LayerCaptureArgs args;
@@ -329,11 +359,11 @@
args.childrenOnly = false;
ScreenCaptureResults captureResults;
- ASSERT_EQ(BAD_VALUE, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
SurfaceComposerClient::Transaction().apply(true);
- ASSERT_EQ(NO_ERROR, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
ScreenCapture sc(captureResults.buffer);
sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
}
@@ -482,9 +512,8 @@
args.layerHandle = redLayerHandle;
ScreenCaptureResults captureResults;
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
// Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
- ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}
TEST_F(ScreenCaptureTest, CaputureSecureLayer) {
@@ -505,15 +534,13 @@
.setLayer(redLayer, INT32_MAX)
.apply();
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-
LayerCaptureArgs args;
args.layerHandle = redLayerHandle;
args.childrenOnly = false;
ScreenCaptureResults captureResults;
// Call from outside system with secure layers will result in permission denied
- ASSERT_EQ(PERMISSION_DENIED, sf->captureLayers(args, captureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults));
UIDFaker f(AID_SYSTEM);
@@ -551,8 +578,7 @@
// From non system uid, can't request screenshot without a specified uid.
UIDFaker f(fakeUid);
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- ASSERT_EQ(PERMISSION_DENIED, composer->captureDisplay(captureArgs, mCaptureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
// Make screenshot request with current uid set. No layers were created with the current
// uid so screenshot will be black.
@@ -581,6 +607,97 @@
mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
}
+TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+
+ const Color layerColor = Color::RED;
+ const Rect bounds = Rect(10, 10, 40, 40);
+
+ Transaction()
+ .show(layer)
+ .hide(mFGSurfaceControl)
+ .setLayerStack(layer, 0)
+ .setLayer(layer, INT32_MAX)
+ .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
+ .setCrop_legacy(layer, bounds)
+ .apply();
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ {
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(bounds, layerColor);
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSkipScreenshot,
+ layer_state_t::eLayerSkipScreenshot)
+ .apply();
+
+ {
+ // Can't screenshot test layer since it now has flag
+ // eLayerSkipScreenshot
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(bounds, {63, 63, 195, 255});
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+}
+
+TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+ sp<SurfaceControl> layer;
+ sp<SurfaceControl> childLayer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+ ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ layer.get()));
+
+ const Color layerColor = Color::RED;
+ const Color childColor = Color::BLUE;
+ const Rect bounds = Rect(10, 10, 40, 40);
+ const Rect childBounds = Rect(20, 20, 30, 30);
+
+ Transaction()
+ .show(layer)
+ .show(childLayer)
+ .hide(mFGSurfaceControl)
+ .setLayerStack(layer, 0)
+ .setLayer(layer, INT32_MAX)
+ .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
+ .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
+ .setCrop_legacy(layer, bounds)
+ .setCrop_legacy(childLayer, childBounds)
+ .apply();
+
+ DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+
+ {
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(childBounds, childColor);
+ mCapture->expectBorder(childBounds, layerColor);
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+
+ Transaction()
+ .setFlags(layer, layer_state_t::eLayerSkipScreenshot,
+ layer_state_t::eLayerSkipScreenshot)
+ .apply();
+
+ {
+ // Can't screenshot child layer since the parent has the flag
+ // eLayerSkipScreenshot
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+ mCapture->expectColor(childBounds, {63, 63, 195, 255});
+ mCapture->expectBorder(childBounds, {63, 63, 195, 255});
+ mCapture->expectBorder(bounds, {63, 63, 195, 255});
+ }
+}
+
TEST_F(ScreenCaptureTest, CaptureLayerWithUid) {
uid_t fakeUid = 12345;
@@ -604,8 +721,7 @@
// From non system uid, can't request screenshot without a specified uid.
std::unique_ptr<UIDFaker> uidFaker = std::make_unique<UIDFaker>(fakeUid);
- sp<ISurfaceComposer> composer = ComposerService::getComposerService();
- ASSERT_EQ(PERMISSION_DENIED, composer->captureLayers(captureArgs, mCaptureResults));
+ ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
// Make screenshot request with current uid set. No layers were created with the current
// uid so screenshot will be black.
@@ -626,7 +742,7 @@
.setLayer(layerWithFakeUid, INT32_MAX)
.setPosition(layerWithFakeUid, 128, 128)
// reparent a layer that was created with a different uid to the new layer.
- .reparent(layer, layerWithFakeUid->getHandle())
+ .reparent(layer, layerWithFakeUid)
.apply();
// Screenshot from the fakeUid caller with the uid requested allows the layer
@@ -720,4 +836,4 @@
verify([&] { screenshot()->expectChildColor(80, 80); });
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 8d97f27..104d919 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -397,16 +397,15 @@
}
void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) {
- t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(),
- DEFERRED_UPDATE);
+ t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl, DEFERRED_UPDATE);
}
void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
- t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+ t.reparent(mBGSurfaceControl, mFGSurfaceControl);
}
void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
- t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z);
+ t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
}
void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) {
@@ -414,7 +413,7 @@
}
void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
- t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+ t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl);
}
void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
@@ -422,7 +421,7 @@
}
void SurfaceInterceptorTest::displayCreation(Transaction&) {
- sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
SurfaceComposerClient::destroyDisplay(testDisplay);
}
@@ -819,7 +818,7 @@
bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() &&
- increment.display_creation().is_secure());
+ !increment.display_creation().is_secure());
if (isMatch && !foundDisplay) {
foundDisplay = true;
} else if (isMatch && foundDisplay) {
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 87fc08a..07c558f 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1322,7 +1322,7 @@
{
TransactionScope ts(*sFakeComposer);
ts.setAlpha(mFGSurfaceControl, 0.75);
- ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+ ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl,
syncSurfaceControl->getSurface()->getNextFrameNumber());
}
EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
@@ -1330,7 +1330,7 @@
{
TransactionScope ts(*sFakeComposer);
ts.setPosition(mFGSurfaceControl, 128, 128);
- ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+ ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl,
syncSurfaceControl->getSurface()->getNextFrameNumber() +
1);
}
@@ -1376,7 +1376,7 @@
TransactionScope ts(*sFakeComposer);
ts.setPosition(relativeSurfaceControl, 64, 64);
ts.show(relativeSurfaceControl);
- ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
+ ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1);
}
auto referenceFrame = mBaseFrame;
// NOTE: All three layers will be visible as the surfaces are
@@ -1606,7 +1606,7 @@
{
TransactionScope ts(*Base::sFakeComposer);
- ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle());
+ ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl);
}
auto referenceFrame2 = referenceFrame;
@@ -1761,7 +1761,7 @@
// Show the child layer in a deferred transaction
{
TransactionScope ts(*Base::sFakeComposer);
- ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(),
+ ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl,
Base::mFGSurfaceControl->getSurface()
->getNextFrameNumber());
ts.show(mChild);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7cc9cef..8097a88 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -39,14 +39,29 @@
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
"DisplayIdentificationTest.cpp",
+ "DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
+ "DisplayDevice_GetBestColorModeTest.cpp",
+ "DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
+ "FrameTimelineTest.cpp",
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
"LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
+ "MessageQueueTest.cpp",
"PromiseTest.cpp",
+ "SurfaceFlinger_CreateDisplayTest.cpp",
+ "SurfaceFlinger_DestroyDisplayTest.cpp",
+ "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_HandleTransactionLockedTest.cpp",
+ "SurfaceFlinger_NotifyPowerBoostTest.cpp",
+ "SurfaceFlinger_OnHotplugReceivedTest.cpp",
+ "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
+ "SurfaceFlinger_SetDisplayStateTest.cpp",
+ "SurfaceFlinger_SetPowerModeInternalTest.cpp",
+ "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"SetFrameRateTest.cpp",
@@ -56,6 +71,7 @@
"RegionSamplingTest.cpp",
"TimeStatsTest.cpp",
"FrameTracerTest.cpp",
+ "TimerTest.cpp",
"TransactionApplicationTest.cpp",
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
@@ -67,19 +83,21 @@
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
- "mock/MockDispSync.cpp",
"mock/MockEventThread.cpp",
+ "mock/MockFrameTracer.cpp",
"mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockSurfaceInterceptor.cpp",
"mock/MockTimeStats.cpp",
- "mock/MockFrameTracer.cpp",
+ "mock/MockVsyncController.cpp",
+ "mock/MockVSyncTracker.cpp",
"mock/system/window/MockNativeWindow.cpp",
],
static_libs: [
"libgmock",
"libcompositionengine",
"libcompositionengine_mocks",
+ "libframetimeline",
"libgui_mocks",
"libperfetto_client_experimental",
"librenderengine_mocks",
@@ -88,7 +106,6 @@
shared_libs: [
"libprotoutil",
"libstatssocket",
- "libsurfaceflinger",
"libtimestats",
"libtimestats_proto",
],
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4843f05..a4f7449 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -35,16 +35,17 @@
#include <utils/String8.h>
#include "BufferQueueLayer.h"
+#include "ContainerLayer.h"
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
#include "mock/MockTimeStats.h"
+#include "mock/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -142,17 +143,19 @@
new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
- auto primaryDispSync = std::make_unique<mock::DispSync>();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
- EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*primaryDispSync, getPeriod())
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
constexpr ISchedulerCallback* kCallback = nullptr;
constexpr bool kHasMultipleConfigs = true;
- mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
- std::move(sfEventThread), kCallback, kHasMultipleConfigs);
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread), kCallback,
+ kHasMultipleConfigs);
// Layer history should be created if there are multiple configs.
ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
@@ -186,6 +189,7 @@
sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
new compositionengine::mock::DisplaySurface();
mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+ std::vector<sp<Layer>> mAuxiliaryLayers;
sp<GraphicBuffer> mBuffer = new GraphicBuffer();
ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
@@ -337,8 +341,6 @@
EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
@@ -447,8 +449,6 @@
template <typename Case>
static void setupCommonCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
// TODO: This seems like an unnecessary call if display is powered off.
EXPECT_CALL(*test->mComposer,
setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
@@ -463,8 +463,6 @@
static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {}
static void setupRECompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-
// TODO: This seems like an unnecessary call if display is powered off.
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -547,7 +545,6 @@
enqueueBuffer(test, layer);
Mock::VerifyAndClearExpectations(test->mMessageQueue);
- EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
bool ignoredRecomputeVisibleRegions;
layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
Mock::VerifyAndClear(test->mRenderEngine);
@@ -580,8 +577,6 @@
.Times(1);
// TODO: Coverage of other values
EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
- // TODO: Coverage of other values
- EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
// These expectations retire on saturation as the code path these
// expectations are for appears to make an extra call to them.
@@ -769,10 +764,9 @@
static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {}
};
-struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> {
- using Base = BaseLayerProperties<SecureLayerProperties>;
-
- static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+template <typename LayerProperties>
+struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> {
+ using Base = BaseLayerProperties<LayerProperties>;
static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
@@ -813,6 +807,13 @@
}
};
+struct ParentSecureLayerProperties
+ : public CommonSecureLayerProperties<ParentSecureLayerProperties> {};
+
+struct SecureLayerProperties : public CommonSecureLayerProperties<SecureLayerProperties> {
+ static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure;
+};
+
struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
using Base = BaseLayerProperties<CursorLayerProperties>;
@@ -848,6 +849,13 @@
Mock::VerifyAndClear(test->mRenderEngine);
Mock::VerifyAndClearExpectations(test->mMessageQueue);
+ initLayerDrawingStateAndComputeBounds(test, layer);
+
+ return layer;
+ }
+
+ template <typename L>
+ static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
layerDrawingState.active.w = 100;
@@ -855,8 +863,6 @@
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
-
- return layer;
}
static void injectLayer(CompositionTest* test, sp<Layer> layer) {
@@ -977,6 +983,49 @@
}
};
+template <typename LayerProperties>
+struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
+ using Base = BaseLayerVariant<LayerProperties>;
+ using FlingerLayerType = sp<ContainerLayer>;
+
+ static FlingerLayerType createLayer(CompositionTest* test) {
+ LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
+ LayerProperties::WIDTH, LayerProperties::HEIGHT,
+ LayerProperties::LAYER_FLAGS, LayerMetadata());
+ FlingerLayerType layer = new ContainerLayer(args);
+ Base::template initLayerDrawingStateAndComputeBounds(test, layer);
+ return layer;
+ }
+};
+
+template <typename LayerVariant, typename ParentLayerVariant>
+struct ChildLayerVariant : public LayerVariant {
+ using Base = LayerVariant;
+ using FlingerLayerType = typename LayerVariant::FlingerLayerType;
+ using ParentBase = ParentLayerVariant;
+
+ static FlingerLayerType createLayer(CompositionTest* test) {
+ // Need to create child layer first. Otherwise layer history size will be 2.
+ FlingerLayerType layer = Base::createLayer(test);
+
+ typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test);
+ parentLayer->addChild(layer);
+ test->mFlinger.setLayerDrawingParent(layer, parentLayer);
+
+ test->mAuxiliaryLayers.push_back(parentLayer);
+
+ return layer;
+ }
+
+ static void cleanupInjectedLayers(CompositionTest* test) {
+ // Clear auxiliary layers first so that child layer can be successfully destroyed in the
+ // following call.
+ test->mAuxiliaryLayers.clear();
+
+ Base::cleanupInjectedLayers(test);
+ }
+};
+
/* ------------------------------------------------------------------------
* Variants to control how the composition type is changed
*/
@@ -1366,6 +1415,38 @@
}
/* ------------------------------------------------------------------------
+ * Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display
+ */
+
+TEST_F(CompositionTest,
+ HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
+ displayRefreshCompositionDirtyGeometry<
+ CompositionCase<InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest,
+ HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
+ displayRefreshCompositionDirtyFrame<
+ CompositionCase<InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
+}
+
+TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
+ captureScreenComposition<
+ CompositionCase<InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ NoCompositionTypeVariant, REScreenshotResultVariant>>();
+}
+
+/* ------------------------------------------------------------------------
* Cursor layers
*/
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index afebc40..54f4c7c 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -27,13 +27,99 @@
#include "AsyncCallRecorder.h"
#include "Scheduler/DispSyncSource.h"
-#include "mock/MockDispSync.h"
+#include "Scheduler/VSyncDispatch.h"
namespace android {
namespace {
using namespace std::chrono_literals;
-using testing::Return;
+using namespace testing;
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+ MOCK_METHOD2(registerCallback,
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
+ MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+
+ MockVSyncDispatch() {
+ ON_CALL(*this, registerCallback)
+ .WillByDefault(
+ [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
+ std::string) {
+ CallbackToken token(mNextToken);
+ mNextToken++;
+
+ mCallbacks.emplace(token, CallbackData(callback));
+ ALOGD("registerCallback: %zu", token.value());
+ return token;
+ });
+
+ ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
+ ALOGD("unregisterCallback: %zu", token.value());
+ mCallbacks.erase(token);
+ });
+
+ ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
+ ALOGD("schedule: %zu", token.value());
+ if (mCallbacks.count(token) == 0) {
+ ALOGD("schedule: callback %zu not registered", token.value());
+ return scheduler::ScheduleResult::Error;
+ }
+
+ auto& callback = mCallbacks.at(token);
+ callback.scheduled = true;
+ callback.vsyncTime = timing.earliestVsync;
+ callback.targetWakeupTime =
+ timing.earliestVsync - timing.workDuration - timing.readyDuration;
+ ALOGD("schedule: callback %zu scheduled", token.value());
+ return scheduler::ScheduleResult::Scheduled;
+ });
+
+ ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
+ ALOGD("cancel: %zu", token.value());
+ if (mCallbacks.count(token) == 0) {
+ ALOGD("cancel: callback %zu is not registered", token.value());
+ return scheduler::CancelResult::Error;
+ }
+
+ auto& callback = mCallbacks.at(token);
+ callback.scheduled = false;
+ ALOGD("cancel: callback %zu cancelled", token.value());
+ return scheduler::CancelResult::Cancelled;
+ });
+ }
+
+ void triggerCallbacks() {
+ ALOGD("triggerCallbacks");
+ for (auto& [token, callback] : mCallbacks) {
+ if (callback.scheduled) {
+ ALOGD("triggerCallbacks: callback %zu", token.value());
+ callback.scheduled = false;
+ callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
+ } else {
+ ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
+ }
+ }
+ }
+
+private:
+ struct CallbackData {
+ explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
+ : func(std::move(func)) {}
+
+ std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
+ bool scheduled = false;
+ nsecs_t vsyncTime = 0;
+ nsecs_t targetWakeupTime = 0;
+ nsecs_t readyTime = 0;
+ };
+
+ std::unordered_map<CallbackToken, CallbackData> mCallbacks;
+ size_t mNextToken;
+};
class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
protected:
@@ -43,15 +129,19 @@
void createDispSync();
void createDispSyncSource();
- void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
+ void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) override;
- std::unique_ptr<mock::DispSync> mDispSync;
- std::unique_ptr<DispSyncSource> mDispSyncSource;
+ std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
+ std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
- AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t, nsecs_t, nsecs_t)> mVSyncEventCallRecorder;
- static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
+ static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
+ static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
static constexpr int mIterations = 100;
+ const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
+ const std::string mName = "DispSyncSourceTest";
};
DispSyncSourceTest::DispSyncSourceTest() {
@@ -66,20 +156,21 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) {
+void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
ALOGD("onVSyncEvent: %" PRId64, when);
- mVSyncEventCallRecorder.recordCall(when);
+ mVSyncEventCallRecorder.recordCall(when, expectedVSyncTimestamp, deadlineTimestamp);
}
void DispSyncSourceTest::createDispSync() {
- mDispSync = std::make_unique<mock::DispSync>();
+ mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
}
void DispSyncSourceTest::createDispSyncSource() {
- createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
- "DispSyncSourceTest");
+ mDispSyncSource =
+ std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration,
+ mReadyDuration, true, mName.c_str());
mDispSyncSource->setCallback(this);
}
@@ -89,57 +180,119 @@
TEST_F(DispSyncSourceTest, createDispSync) {
createDispSync();
- EXPECT_TRUE(mDispSync);
+ EXPECT_TRUE(mVSyncDispatch);
}
TEST_F(DispSyncSourceTest, createDispSyncSource) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
+ .WillOnce(Return(scheduler::CancelResult::Cancelled));
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
}
TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
// DispSyncSource starts with Vsync disabled
- mDispSync->triggerCallback();
+ mVSyncDispatch->triggerCallbacks();
EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
}
TEST_F(DispSyncSourceTest, waitForCallbacks) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch,
+ schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == mWorkDuration.count() &&
+ timings.readyDuration == mReadyDuration.count();
+ })))
+ .Times(mIterations + 1);
+ EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
mDispSyncSource->setVSyncEnabled(true);
- EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
-
for (int i = 0; i < mIterations; i++) {
- mDispSync->triggerCallback();
- EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ mVSyncDispatch->triggerCallbacks();
+ const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+ ASSERT_TRUE(callbackData.has_value());
+ const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+ EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
}
}
-TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) {
+TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
+ createDispSync();
+
+ InSequence seq;
+ EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch,
+ schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == mWorkDuration.count() &&
+ timings.readyDuration == mReadyDuration.count();
+ })))
+ .Times(1);
+
createDispSyncSource();
+
EXPECT_TRUE(mDispSyncSource);
mDispSyncSource->setVSyncEnabled(true);
- EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count());
-
+ EXPECT_CALL(*mVSyncDispatch,
+ schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == mWorkDuration.count() &&
+ timings.readyDuration == mReadyDuration.count();
+ })))
+ .Times(mIterations);
for (int i = 0; i < mIterations; i++) {
- mDispSync->triggerCallback();
- EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ mVSyncDispatch->triggerCallbacks();
+ const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+ ASSERT_TRUE(callbackData.has_value());
+ const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+ EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count());
}
- EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666));
- mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count());
+ const auto newDuration = mWorkDuration / 2;
+ EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == newDuration.count() &&
+ timings.readyDuration == 0;
+ })))
+ .Times(1);
+ mDispSyncSource->setDuration(newDuration, 0ns);
- EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count());
-
+ EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
+ return timings.workDuration == newDuration.count() &&
+ timings.readyDuration == 0;
+ })))
+ .Times(mIterations);
for (int i = 0; i < mIterations; i++) {
- mDispSync->triggerCallback();
- EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value());
+ mVSyncDispatch->triggerCallbacks();
+ const auto callbackData = mVSyncEventCallRecorder.waitForCall();
+ ASSERT_TRUE(callbackData.has_value());
+ const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value();
+ EXPECT_EQ(when, expectedVSyncTimestamp - newDuration.count());
}
+
+ EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
+ EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
new file mode 100644
index 0000000..2e53cd1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+ void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+ void addHwcColorModesMapping(ui::ColorMode colorMode,
+ std::vector<ui::RenderIntent> renderIntents) {
+ mHwcColorModes[colorMode] = renderIntents;
+ }
+
+ void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+ void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+ void getBestColorMode() {
+ auto displayDevice =
+ injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setHwcColorModes(mHwcColorModes);
+ injector.setHasWideColorGamut(mHasWideColorGamut);
+ injector.setNativeWindow(mNativeWindow);
+ });
+
+ displayDevice->getCompositionDisplay()
+ ->getDisplayColorProfile()
+ ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+ &mOutColorMode, &mOutRenderIntent);
+ }
+
+ ui::Dataspace mOutDataspace;
+ ui::ColorMode mOutColorMode;
+ ui::RenderIntent mOutRenderIntent;
+
+private:
+ ui::Dataspace mInputDataspace;
+ ui::RenderIntent mInputRenderIntent;
+ bool mHasWideColorGamut = false;
+ std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
new file mode 100644
index 0000000..9fe30f8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * 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"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+ static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
+ static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+ HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+ DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+ ui::Rotation physicalOrientation)
+ : mFlingerDisplaySize(flingerDisplaySize),
+ mHardwareDisplaySize(hardwareDisplaySize),
+ mPhysicalOrientation(physicalOrientation),
+ mDisplayDevice(createDisplayDevice()) {}
+
+ sp<DisplayDevice> createDisplayDevice() {
+ return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setPhysicalOrientation(mPhysicalOrientation);
+ });
+ }
+
+ ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+ void setProjectionForRotation0() {
+ // A logical rotation of 0 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation90() {
+ // A logical rotation of 90 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
+ }
+
+ void setProjectionForRotation180() {
+ // A logical rotation of 180 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation270() {
+ // A logical rotation of 270 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
+ }
+
+ void expectStateForHardwareTransform0() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform90() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform180() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform270() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ const ui::Size mFlingerDisplaySize;
+ const ui::Size mHardwareDisplaySize;
+ const ui::Rotation mPhysicalOrientation;
+ const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed0()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed90()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed180()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed270()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform180();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
new file mode 100644
index 0000000..be7609a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <vector>
+
+#include <ui/DisplayId.h>
+#include "DisplayIdGenerator.h"
+
+namespace android {
+
+template <typename T>
+void testNextId(DisplayIdGenerator<T>& generator) {
+ constexpr int kNumIds = 5;
+ std::vector<T> ids;
+ for (int i = 0; i < kNumIds; i++) {
+ const auto id = generator.nextId();
+ ASSERT_TRUE(id);
+ ids.push_back(*id);
+ }
+
+ // All IDs should be different.
+ for (size_t i = 0; i < kNumIds; i++) {
+ for (size_t j = i + 1; j < kNumIds; j++) {
+ EXPECT_NE(ids[i], ids[j]);
+ }
+ }
+}
+
+TEST(DisplayIdGeneratorTest, nextIdGpuVirtual) {
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> generator;
+ testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, nextIdHalVirtual) {
+ RandomDisplayIdGenerator<HalVirtualDisplayId> generator;
+ testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, markUnused) {
+ constexpr size_t kMaxIdsCount = 5;
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+ const auto id = generator.nextId();
+ EXPECT_TRUE(id);
+
+ for (int i = 1; i < kMaxIdsCount; i++) {
+ EXPECT_TRUE(generator.nextId());
+ }
+
+ EXPECT_FALSE(generator.nextId());
+
+ generator.markUnused(*id);
+ EXPECT_TRUE(generator.nextId());
+}
+
+TEST(DisplayIdGeneratorTest, maxIdsCount) {
+ constexpr size_t kMaxIdsCount = 5;
+ RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+ for (int i = 0; i < kMaxIdsCount; i++) {
+ EXPECT_TRUE(generator.nextId());
+ }
+
+ EXPECT_FALSE(generator.nextId());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 7fade0d..db05d5a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -14,157 +14,22 @@
* 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 "LibSurfaceFlingerUnittests"
-#include <type_traits>
-
-#include <android/hardware/power/Boost.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/mock/Display.h>
-#include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/DisplaySurface.h>
-#include <compositionengine/mock/RenderSurface.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/mock/GraphicBufferConsumer.h>
-#include <gui/mock/GraphicBufferProducer.h>
-#include <log/log.h>
-#include <renderengine/mock/RenderEngine.h>
-#include <ui/DebugUtils.h>
-
-#include "DisplayIdentificationTest.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
-#include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSchedulerCallback.h"
-#include "mock/MockSurfaceInterceptor.h"
-#include "mock/system/window/MockNativeWindow.h"
+#include "DisplayTransactionTestHelpers.h"
namespace android {
-namespace {
-namespace hal = android::hardware::graphics::composer::hal;
-
-using android::hardware::power::Boost;
-
-using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::Mock;
-using testing::ResultOf;
using testing::Return;
-using testing::ReturnRefOfCopy;
using testing::SetArgPointee;
-using testing::StrictMock;
-using hal::ColorMode;
-using hal::Connection;
-using hal::DisplayCapability;
-using hal::DisplayType;
-using hal::Error;
-using hal::Hdr;
-using hal::HWDisplayId;
-using hal::IComposer;
-using hal::IComposerClient;
-using hal::PerFrameMetadataKey;
-using hal::PowerMode;
-using hal::RenderIntent;
+using android::hardware::graphics::composer::hal::HWDisplayId;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
-using HWC2Display = TestableSurfaceFlinger::HWC2Display;
-
-constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
-constexpr int32_t DEFAULT_DPI = 320;
-constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
-
-constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
-
-/* ------------------------------------------------------------------------
- * Boolean avoidance
- *
- * To make calls and template instantiations more readable, we define some
- * local enums along with an implicit bool conversion.
- */
-
-#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
-
-BOOL_SUBSTITUTE(Async);
-BOOL_SUBSTITUTE(Critical);
-BOOL_SUBSTITUTE(Primary);
-BOOL_SUBSTITUTE(Secure);
-BOOL_SUBSTITUTE(Virtual);
-
-/* ------------------------------------------------------------------------
- *
- */
-
-class DisplayTransactionTest : public testing::Test {
-public:
- DisplayTransactionTest();
- ~DisplayTransactionTest() override;
-
- // --------------------------------------------------------------------
- // Mock/Fake injection
-
- void injectMockScheduler();
- void injectMockComposer(int virtualDisplayCount);
- void injectFakeBufferQueueFactory();
- void injectFakeNativeWindowSurfaceFactory();
- sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
-
- // --------------------------------------------------------------------
- // Postcondition helpers
-
- bool hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId);
- bool hasTransactionFlagSet(int flag);
- bool hasDisplayDevice(sp<IBinder> displayToken);
- sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
- bool hasCurrentDisplayState(sp<IBinder> displayToken);
- const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
- bool hasDrawingDisplayState(sp<IBinder> displayToken);
- const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
-
- // --------------------------------------------------------------------
- // Test instances
-
- TestableSurfaceFlinger mFlinger;
- sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
- sp<GraphicBuffer> mBuffer = new GraphicBuffer();
- Hwc2::mock::PowerAdvisor mPowerAdvisor;
-
- // 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;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
- mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
-
- mock::DispSync* mPrimaryDispSync = new mock::DispSync;
- mock::SchedulerCallback mSchedulerCallback;
- mock::EventThread* mEventThread = new mock::EventThread;
- mock::EventThread* mSFEventThread = new mock::EventThread;
-
- // These mocks are created only when expected to be created via a factory.
- sp<mock::GraphicBufferConsumer> mConsumer;
- sp<mock::GraphicBufferProducer> mProducer;
- surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-};
DisplayTransactionTest::DisplayTransactionTest() {
const ::testing::TestInfo* const test_info =
@@ -191,7 +56,7 @@
injectMockScheduler();
mFlinger.mutableEventQueue().reset(mMessageQueue);
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
- mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
+ mFlinger.mutableInterceptor() = mSurfaceInterceptor;
injectMockComposer(0);
}
@@ -213,7 +78,8 @@
.WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
- mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
+ mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
+ std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
std::unique_ptr<EventThread>(mEventThread),
std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback);
}
@@ -326,3341 +192,4 @@
return mFlinger.mutableDrawingState().displays.valueFor(displayToken);
}
-/* ------------------------------------------------------------------------
- *
- */
-
-template <typename PhysicalDisplay>
-struct PhysicalDisplayIdType {};
-
-template <uint64_t displayId>
-using VirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
-
-struct NoDisplayId {};
-
-template <typename>
-struct IsPhysicalDisplayId : std::bool_constant<false> {};
-
-template <typename PhysicalDisplay>
-struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
-
-template <typename>
-struct DisplayIdGetter;
-
-template <typename PhysicalDisplay>
-struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static std::optional<PhysicalDisplayId> get() {
- if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
- ? LEGACY_DISPLAY_TYPE_PRIMARY
- : LEGACY_DISPLAY_TYPE_EXTERNAL);
- }
-
- const auto info =
- parseDisplayIdentificationData(PhysicalDisplay::PORT,
- PhysicalDisplay::GET_IDENTIFICATION_DATA());
- return info ? std::make_optional(info->id) : std::nullopt;
- }
-};
-
-template <uint64_t displayId>
-struct DisplayIdGetter<VirtualDisplayIdType<displayId>> {
- // TODO(b/160679868) Use VirtualDisplayId
- static std::optional<PhysicalDisplayId> get() { return PhysicalDisplayId{displayId}; }
-};
-
-template <>
-struct DisplayIdGetter<NoDisplayId> {
- static std::optional<DisplayId> get() { return {}; }
-};
-
-template <typename>
-struct DisplayConnectionTypeGetter {
- static constexpr std::optional<DisplayConnectionType> value;
-};
-
-template <typename PhysicalDisplay>
-struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
-};
-
-template <typename>
-struct HwcDisplayIdGetter {
- static constexpr std::optional<HWDisplayId> value;
-};
-
-constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
-
-template <uint64_t displayId>
-struct HwcDisplayIdGetter<VirtualDisplayIdType<displayId>> {
- static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
-};
-
-template <typename PhysicalDisplay>
-struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
-};
-
-// DisplayIdType can be:
-// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
-// 2) VirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
-// 3) NoDisplayId for virtual display without HWC backing.
-template <typename DisplayIdType, int width, int height, Critical critical, Async async,
- Secure secure, Primary primary, int grallocUsage>
-struct DisplayVariant {
- using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
- using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
- using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
-
- // The display width and height
- static constexpr int WIDTH = width;
- static constexpr int HEIGHT = height;
-
- static constexpr int GRALLOC_USAGE = grallocUsage;
-
- // Whether the display is virtual or physical
- static constexpr Virtual VIRTUAL =
- IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
-
- // When creating native window surfaces for the framebuffer, whether those should be critical
- static constexpr Critical CRITICAL = critical;
-
- // When creating native window surfaces for the framebuffer, whether those should be async
- static constexpr Async ASYNC = async;
-
- // Whether the display should be treated as secure
- static constexpr Secure SECURE = secure;
-
- // Whether the display is primary
- static constexpr Primary PRIMARY = primary;
-
- static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
- if (auto displayId = DISPLAY_ID::get()) {
- ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
- } else {
- ceDisplayArgs.setUseHwcVirtualDisplays(false);
- }
- ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs.build());
-
- auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
- static_cast<bool>(PRIMARY));
-
- injector.setSecure(static_cast<bool>(SECURE));
- injector.setNativeWindow(test->mNativeWindow);
-
- // Creating a DisplayDevice requires getting default dimensions from the
- // native window along with some other initial setup.
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
- .WillRepeatedly(Return(0));
-
- return injector;
- }
-
- // Called by tests to set up any native window creation call expectations.
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
- .WillOnce(Return(test->mNativeWindow));
-
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
- .WillRepeatedly(Return(0));
- }
-
- static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
- .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
- .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
- .WillRepeatedly(Return(NO_ERROR));
- }
-
- static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
- }
-};
-
-template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
- typename PhysicalDisplay = void>
-struct HwcDisplayVariant {
- // The display id supplied by the HWC
- static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
-
- // The HWC display type
- static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
-
- // The HWC active configuration id
- static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
- static constexpr PowerMode INIT_POWER_MODE = PowerMode::ON;
-
- static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
- test->mFlinger.mutablePendingHotplugEvents().emplace_back(
- HotplugEvent{HWC_DISPLAY_ID, connection});
- }
-
- // Called by tests to inject a HWC display setup
- static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
- const auto displayId = DisplayVariant::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
- static_cast<bool>(DisplayVariant::PRIMARY))
- .setHwcDisplayId(HWC_DISPLAY_ID)
- .setWidth(DisplayVariant::WIDTH)
- .setHeight(DisplayVariant::HEIGHT)
- .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
- .inject(&test->mFlinger, test->mComposer);
- }
-
- // Called by tests to inject a HWC display setup
- 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);
- }
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
- PhysicalDisplay::CONNECTION_TYPE})
- .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
- .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- }
-
- static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
- constexpr auto CONNECTION_TYPE =
- PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
- ? IComposerClient::DisplayConnectionType::INTERNAL
- : IComposerClient::DisplayConnectionType::EXTERNAL;
-
- EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
-
- EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
- .WillOnce(Return(hal::Error::NONE));
- EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::VSYNC_PERIOD, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::DPI_X, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::DPI_Y, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::CONFIG_GROUP, _))
- .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
-
- if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
- SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
- Return(Error::NONE)));
- } else {
- EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
- .WillOnce(Return(Error::UNSUPPORTED));
- }
- }
-
- // Called by tests to set up HWC call expectations
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
- }
-};
-
-// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
-constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-
-template <typename PhysicalDisplay, int width, int height, Critical critical>
-struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
- Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
- GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
- critical, Async::FALSE, Secure::TRUE,
- PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- PhysicalDisplay> {};
-
-template <bool hasIdentificationData>
-struct PrimaryDisplay {
- static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
- static constexpr Primary PRIMARY = Primary::TRUE;
- static constexpr uint8_t PORT = 255;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
- static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
-};
-
-template <bool hasIdentificationData>
-struct ExternalDisplay {
- static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
- static constexpr Primary PRIMARY = Primary::FALSE;
- static constexpr uint8_t PORT = 254;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
- static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-struct TertiaryDisplay {
- static constexpr Primary PRIMARY = Primary::FALSE;
- static constexpr uint8_t PORT = 253;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-// A primary display is a physical display that is critical
-using PrimaryDisplayVariant =
- PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
-
-// An external display is physical display that is not critical.
-using ExternalDisplayVariant =
- PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
-
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
-
-// A virtual display not supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
-
-template <int width, int height, Secure secure>
-struct NonHwcVirtualDisplayVariant
- : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
- using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
-
- static void injectHwcDisplay(DisplayTransactionTest*) {}
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPixels({Base::WIDTH, Base::HEIGHT})
- .setIsSecure(static_cast<bool>(Base::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- }
-
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
- }
-
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- Base::setupNativeWindowSurfaceCreationCallExpectations(test);
- EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
- }
-};
-
-// A virtual display supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
-
-template <int width, int height, Secure secure>
-struct HwcVirtualDisplayVariant
- : DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE,
- GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
- using Base = DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
- using Self = HwcVirtualDisplayVariant<width, height, secure>;
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(false)
- .setPixels({Base::WIDTH, Base::HEIGHT})
- .setIsSecure(static_cast<bool>(Base::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
-
- // Insert display data so that the HWC thinks it created the virtual display.
- if (const auto displayId = Base::DISPLAY_ID::get()) {
- test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
- }
-
- return compositionDisplay;
- }
-
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- Base::setupNativeWindowSurfaceCreationCallExpectations(test);
- EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
- }
-
- static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
- .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
- }
-};
-
-// For this variant, SurfaceFlinger should not configure itself with wide
-// display support, so the display should not be configured for wide-color
-// support.
-struct WideColorSupportNotConfiguredVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableHasWideColorDisplay() = false;
- test->mFlinger.mutableUseColorManagement() = 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);
- }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, and the display should respond with an non-empty list of supported
-// color modes. Wide-color support should be configured.
-template <typename Display>
-struct WideColorP3ColorimetricSupportedVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = true;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
- test->mFlinger.mutableHasWideColorDisplay() = 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>(
- std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
- RenderIntent::COLORIMETRIC))
- .WillOnce(Return(Error::NONE));
- }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, but the display should respond with an empty list of supported color
-// modes. Wide-color support for the display should not be configured.
-template <typename Display>
-struct WideColorNotSupportedVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
- test->mFlinger.mutableHasWideColorDisplay() = 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);
- }
-};
-
-// For this variant, the display is not a HWC display, so no HDR support should
-// be configured.
-struct NonHwcDisplayHdrSupportVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
- }
-};
-
-template <typename Display>
-struct Hdr10PlusSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = true;
- static constexpr bool HDR10_SUPPORTED = true;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
- Hdr::HDR10_PLUS,
- Hdr::HDR10,
- })),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HDR10, so HDR10 support should be configured.
-template <typename Display>
-struct Hdr10SupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = true;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HLG, so HLG support should be configured.
-template <typename Display>
-struct HdrHlgSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = true;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(
- DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
-template <typename Display>
-struct HdrDolbyVisionSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with am empty list of HDR
-// modes, so no HDR support should be configured.
-template <typename Display>
-struct HdrNotSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
- }
-};
-
-struct NonHwcPerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = 0;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
- }
-};
-
-template <typename Display>
-struct NoPerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = 0;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
- }
-};
-
-template <typename Display>
-struct Smpte2086PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
- PerFrameMetadataKey::WHITE_POINT_X,
- PerFrameMetadataKey::WHITE_POINT_Y,
- PerFrameMetadataKey::MAX_LUMINANCE,
- PerFrameMetadataKey::MIN_LUMINANCE,
- })));
- }
-};
-
-template <typename Display>
-struct Cta861_3_PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
- PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
- })));
- }
-};
-
-template <typename Display>
-struct Hdr10_Plus_PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::HDR10_PLUS_SEI,
- })));
- }
-};
-/* ------------------------------------------------------------------------
- * Typical display configurations to test
- */
-
-template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
- typename PerFrameMetadataSupportPolicy>
-struct Case {
- using Display = DisplayPolicy;
- using WideColorSupport = WideColorSupportPolicy;
- using HdrSupport = HdrSupportPolicy;
- using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
-};
-
-using SimplePrimaryDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using SimpleExternalDisplayCase =
- Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
- HdrNotSupportedVariant<ExternalDisplayVariant>,
- NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
-using SimpleTertiaryDisplayCase =
- Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
- HdrNotSupportedVariant<TertiaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
-using NonHwcVirtualDisplayCase =
- Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
- WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
- NonHwcPerFrameMetadataSupportVariant>;
-using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
-using HwcVirtualDisplayCase =
- Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
- HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
- NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
-using WideColorP3ColorimetricDisplayCase =
- Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10PlusDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>,
- Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrHlgDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrHlgSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrDolbyVisionDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrSmpte2086DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrCta861_3_DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-
-/* ------------------------------------------------------------------------
- *
- * SurfaceFlinger::onHotplugReceived
- */
-
-TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
- constexpr int currentSequenceId = 123;
- constexpr HWDisplayId hwcDisplayId1 = 456;
- constexpr HWDisplayId hwcDisplayId2 = 654;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Simulate two hotplug events (a connect and a disconnect)
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // All events should be in the pending event queue.
- const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
- ASSERT_EQ(2u, pendingEvents.size());
- EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
- EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
- EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
- EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
-}
-
-TEST_F(DisplayTransactionTest, hotplugDiscardsUnexpectedEvents) {
- constexpr int currentSequenceId = 123;
- constexpr int otherSequenceId = 321;
- constexpr HWDisplayId displayId = 456;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We do not expect any calls to invalidate().
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Call with an unexpected sequence id
- mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should not be set
- EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no pending events
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-TEST_F(DisplayTransactionTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
- constexpr int currentSequenceId = 123;
- constexpr HWDisplayId displayId1 = 456;
-
- // --------------------------------------------------------------------
- // Note:
- // --------------------------------------------------------------------
- // This test case is a bit tricky. We want to verify that
- // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
- // don't really want to provide coverage for everything the later function
- // does as there are specific tests for it.
- // --------------------------------------------------------------------
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does appear to be the
- // main thread.
- mFlinger.mutableMainThreadId() = std::this_thread::get_id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Simulate a disconnect on a display id that is not connected. This should
- // be enqueued by onHotplugReceived(), and dequeued by
- // processDisplayHotplugEventsLocked(), but then ignored as invalid.
- mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no event queued on return, as it should have been
- // processed.
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::createDisplay
- */
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
- const String8 name("virtual.test");
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been added to the current state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_TRUE(display.isVirtual());
- EXPECT_FALSE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForSecureDisplay) {
- const String8 name("virtual.test");
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been added to the current state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_TRUE(display.isVirtual());
- EXPECT_TRUE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::destroyDisplay
- */
-
-TEST_F(DisplayTransactionTest, destroyDisplayClearsCurrentStateForDisplay) {
- using Case = NonHwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A virtual display exists
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.destroyDisplay(existing.token());
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been removed from the current state
- EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
-
- // Ths display should still exist in the drawing state
- EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
-
- // The display transaction needed flasg should be set
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-}
-
-TEST_F(DisplayTransactionTest, destroyDisplayHandlesUnknownDisplay) {
- // --------------------------------------------------------------------
- // Preconditions
-
- sp<BBinder> displayToken = new BBinder();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.destroyDisplay(displayToken);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::resetDisplayState
- */
-
-TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) {
- using Case = NonHwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // vsync is enabled and available
- mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true;
- mFlinger.scheduler()->mutableHWVsyncAvailable() = true;
-
- // A display exists
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call disable vsyncs
- EXPECT_CALL(mSchedulerCallback, setVsyncEnabled(false)).Times(1);
-
- // The call ends any display resyncs
- EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.resetDisplayState();
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // vsyncs should be off and not available.
- EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled());
- EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable());
-
- // The display should have been removed from the display map.
- EXPECT_FALSE(hasDisplayDevice(existing.token()));
-
- // The display should still exist in the current state
- EXPECT_TRUE(hasCurrentDisplayState(existing.token()));
-
- // The display should have been removed from the drawing state
- EXPECT_FALSE(hasDrawingDisplayState(existing.token()));
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::notifyPowerBoost
- */
-
-TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
- 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
-
- std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
- EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
- std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
- std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
- EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
- std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
- EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::GetBestColorMode
- */
-class GetBestColorModeTest : public DisplayTransactionTest {
-public:
- void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
-
- void addHwcColorModesMapping(ui::ColorMode colorMode,
- std::vector<ui::RenderIntent> renderIntents) {
- mHwcColorModes[colorMode] = renderIntents;
- }
-
- void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
-
- void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
-
- void getBestColorMode() {
- auto displayDevice =
- injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
- injector.setHwcColorModes(mHwcColorModes);
- injector.setHasWideColorGamut(mHasWideColorGamut);
- injector.setNativeWindow(mNativeWindow);
- });
-
- displayDevice->getCompositionDisplay()
- ->getDisplayColorProfile()
- ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
- &mOutColorMode, &mOutRenderIntent);
- }
-
- ui::Dataspace mOutDataspace;
- ui::ColorMode mOutColorMode;
- ui::RenderIntent mOutRenderIntent;
-
-private:
- ui::Dataspace mInputDataspace;
- ui::RenderIntent mInputRenderIntent;
- bool mHasWideColorGamut = false;
- std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
-};
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
- addHwcColorModesMapping(ui::ColorMode::SRGB,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- addHwcColorModesMapping(ui::ColorMode::SRGB,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::setProjection
- */
-
-class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
-public:
- static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
- static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
-
- static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
- HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
-
- DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
- ui::Rotation physicalOrientation)
- : mFlingerDisplaySize(flingerDisplaySize),
- mHardwareDisplaySize(hardwareDisplaySize),
- mPhysicalOrientation(physicalOrientation),
- mDisplayDevice(createDisplayDevice()) {}
-
- sp<DisplayDevice> createDisplayDevice() {
- return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
- injector.setPhysicalOrientation(mPhysicalOrientation);
- });
- }
-
- ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
-
- void setProjectionForRotation0() {
- // A logical rotation of 0 uses the SurfaceFlinger display size
- mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
- Rect(mFlingerDisplaySize));
- }
-
- void setProjectionForRotation90() {
- // A logical rotation of 90 uses the SurfaceFlinger display size with
- // the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
- Rect(SwapWH(mFlingerDisplaySize)));
- }
-
- void setProjectionForRotation180() {
- // A logical rotation of 180 uses the SurfaceFlinger display size
- mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
- Rect(mFlingerDisplaySize));
- }
-
- void setProjectionForRotation270() {
- // A logical rotation of 270 uses the SurfaceFlinger display size with
- // the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
- Rect(SwapWH(mFlingerDisplaySize)));
- }
-
- void expectStateForHardwareTransform0() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform90() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
- // size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform180() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform270() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
- // size width and height swapped
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- const ui::Size mFlingerDisplaySize;
- const ui::Size mHardwareDisplaySize;
- const ui::Rotation mPhysicalOrientation;
- const sp<DisplayDevice> mDisplayDevice;
-};
-
-struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed0()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_0) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform270();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed90()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_90) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform0();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed180()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_180) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform90();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed270()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_270) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform180();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::getDisplayNativePrimaries
- */
-
-class GetDisplayNativePrimaries : public DisplayTransactionTest {
-public:
- GetDisplayNativePrimaries();
- void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
- void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
-
-private:
- static constexpr float mStartingTestValue = 1.0f;
-};
-
-GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
- SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
- injectFakeNativeWindowSurfaceFactory();
-}
-
-void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
- ui::DisplayPrimaries& primaries) {
- float startingVal = mStartingTestValue;
- primaries.red.X = startingVal++;
- primaries.red.Y = startingVal++;
- primaries.red.Z = startingVal++;
- primaries.green.X = startingVal++;
- primaries.green.Y = startingVal++;
- primaries.green.Z = startingVal++;
- primaries.blue.X = startingVal++;
- primaries.blue.Y = startingVal++;
- primaries.blue.Z = startingVal++;
- primaries.white.X = startingVal++;
- primaries.white.Y = startingVal++;
- primaries.white.Z = startingVal++;
-}
-
-void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
- const ui::DisplayPrimaries& primaries) {
- float startingVal = mStartingTestValue;
- EXPECT_EQ(primaries.red.X, startingVal++);
- EXPECT_EQ(primaries.red.Y, startingVal++);
- EXPECT_EQ(primaries.red.Z, startingVal++);
- EXPECT_EQ(primaries.green.X, startingVal++);
- EXPECT_EQ(primaries.green.Y, startingVal++);
- EXPECT_EQ(primaries.green.Z, startingVal++);
- EXPECT_EQ(primaries.blue.X, startingVal++);
- EXPECT_EQ(primaries.blue.Y, startingVal++);
- EXPECT_EQ(primaries.blue.Z, startingVal++);
- EXPECT_EQ(primaries.white.X, startingVal++);
- EXPECT_EQ(primaries.white.Y, startingVal++);
- EXPECT_EQ(primaries.white.Z, startingVal++);
-}
-
-TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
- ui::DisplayPrimaries primaries;
- EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
-}
-
-TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
- auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
- injector.inject();
- auto internalDisplayToken = injector.token();
-
- ui::DisplayPrimaries expectedPrimaries;
- populateDummyDisplayNativePrimaries(expectedPrimaries);
- mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
-
- ui::DisplayPrimaries primaries;
- EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
-
- checkDummyDisplayNativePrimaries(primaries);
-}
-
-TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
- sp<BBinder> notInternalDisplayToken = new BBinder();
-
- ui::DisplayPrimaries primaries;
- populateDummyDisplayNativePrimaries(primaries);
- EXPECT_EQ(NAME_NOT_FOUND,
- mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
-
- // Check primaries argument wasn't modified in case of failure
- checkDummyDisplayNativePrimaries(primaries);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setupNewDisplayDeviceInternal
- */
-
-class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
-public:
- template <typename T>
- void setupNewDisplayDeviceInternalTest();
-};
-
-template <typename Case>
-void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
- const sp<BBinder> displayToken = new BBinder();
- const sp<compositionengine::mock::DisplaySurface> displaySurface =
- new compositionengine::mock::DisplaySurface();
- const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Wide color displays support is configured appropriately
- Case::WideColorSupport::injectConfigChange(this);
-
- // The display is setup with the HWC.
- Case::Display::injectHwcDisplay(this);
-
- // SurfaceFlinger will use a test-controlled factory for native window
- // surfaces.
- injectFakeNativeWindowSurfaceFactory();
-
- // A compositionengine::Display has already been created
- auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // Various native window calls will be made.
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- DisplayDeviceState state;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
- ASSERT_TRUE(hwcDisplayId);
- state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
- .type = *connectionType,
- .hwcDisplayId = *hwcDisplayId};
- }
-
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
- displaySurface, producer);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- ASSERT_TRUE(device != nullptr);
- EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
- EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
- EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
- EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
- EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
- EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
- EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
- EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
- EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
- EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
- // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
- // remapped, and the test only ever sets up one config. If there were an error
- // looking up the remapped index, device->getActiveConfig() would be -1 instead.
- EXPECT_EQ(0, device->getActiveConfig().value());
- EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
- device->getSupportedPerFrameMetadata());
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
- setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
- setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
- setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
- setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
- setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
- setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
- setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
- setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
- setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
- setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
- setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
- */
-
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
- template <typename Case>
- void setupCommonPreconditions();
-
- template <typename Case, bool connected>
- static void expectHotplugReceived(mock::EventThread*);
-
- template <typename Case>
- void setupCommonCallExpectationsForConnectProcessing();
-
- template <typename Case>
- void setupCommonCallExpectationsForDisconnectProcessing();
-
- template <typename Case>
- void processesHotplugConnectCommon();
-
- template <typename Case>
- void ignoresHotplugConnectCommon();
-
- template <typename Case>
- void processesHotplugDisconnectCommon();
-
- template <typename Case>
- void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
-
- template <typename Case>
- void verifyPhysicalDisplayIsConnected();
-
- void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
-};
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
- // Wide color displays support is configured appropriately
- Case::WideColorSupport::injectConfigChange(this);
-
- // SurfaceFlinger will use a test-controlled factory for BufferQueues
- injectFakeBufferQueueFactory();
-
- // SurfaceFlinger will use a test-controlled factory for native window
- // surfaces.
- injectFakeNativeWindowSurfaceFactory();
-}
-
-template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
- const auto convert = [](auto physicalDisplayId) {
- return std::make_optional(DisplayId{physicalDisplayId});
- };
-
- EXPECT_CALL(*eventThread,
- onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
- .Times(1);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
- Case::Display::setupHwcHotplugCallExpectations(this);
-
- Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
- Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
- Case::WideColorSupport::setupComposerCallExpectations(this);
- 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 HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
- expectHotplugReceived<Case, false>(mEventThread);
- expectHotplugReceived<Case, false>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
- // The display device should have been set up in the list of displays.
- ASSERT_TRUE(hasDisplayDevice(displayToken));
- const auto& device = getDisplayDevice(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
-
- std::optional<DisplayDeviceState::Physical> expectedPhysical;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
- ASSERT_TRUE(hwcDisplayId);
- expectedPhysical = {.id = static_cast<PhysicalDisplayId>(*displayId),
- .type = *connectionType,
- .hwcDisplayId = *hwcDisplayId};
- }
-
- // The display should have been set up in the current display state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& current = getCurrentDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
- EXPECT_EQ(expectedPhysical, current.physical);
-
- // The display should have been set up in the drawing display state
- ASSERT_TRUE(hasDrawingDisplayState(displayToken));
- const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
- EXPECT_EQ(expectedPhysical, draw.physical);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
- // HWComposer should have an entry for the display
- EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
- auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId];
-
- verifyDisplayIsConnected<Case>(displayToken);
-}
-
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
- EXPECT_FALSE(hasDisplayDevice(displayToken));
- EXPECT_FALSE(hasCurrentDisplayState(displayToken));
- EXPECT_FALSE(hasDrawingDisplayState(displayToken));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillOnce(Return(false));
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- verifyPhysicalDisplayIsConnected<Case>();
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
- EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
- .Times(0);
-
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
- processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest,
- processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
- // Inject an external display.
- ExternalDisplayVariant::injectHwcDisplay(this);
-
- processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
- // Inject a primary display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
-
- processesHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
- // Inject both a primary and external display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
- ExternalDisplayVariant::injectHwcDisplay(this);
-
- // TODO: This is an unnecessary call.
- EXPECT_CALL(*mComposer,
- getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
- SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
- Return(Error::NONE)));
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
- ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfExternalForVrComposer) {
- // Inject a primary display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(true));
-
- ignoresHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
- processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
- processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
- // A hotplug disconnect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- // A hotplug connect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
- EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]);
-
- // A new display should be connected in its place
-
- verifyPhysicalDisplayIsConnected<Case>();
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // The HWC supports at least one virtual display
- injectMockComposer(1);
-
- setupCommonPreconditions<Case>();
-
- // A virtual display was added to the current state, and it has a
- // surface(producer)
- sp<BBinder> displayToken = new BBinder();
-
- DisplayDeviceState state;
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
- state.surface = surface;
- mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
- Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
-
- EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
-
- EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
- EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
-
- Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display device should have been set up in the list of displays.
- verifyDisplayIsConnected<Case>(displayToken);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
- // Cleanup
- mFlinger.mutableCurrentState().displays.removeItem(displayToken);
- mFlinger.mutableDrawingState().displays.removeItem(displayToken);
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // The HWC supports at least one virtual display
- injectMockComposer(1);
-
- setupCommonPreconditions<Case>();
-
- // A virtual display was added to the current state, but it does not have a
- // surface.
- sp<BBinder> displayToken = new BBinder();
-
- DisplayDeviceState state;
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // There will not be a display device set up.
- EXPECT_FALSE(hasDisplayDevice(displayToken));
-
- // The drawing display state will be set from the current display state.
- ASSERT_TRUE(hasDrawingDisplayState(displayToken));
- const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A virtual display is set up but is removed from the current state.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
- mFlinger.mutableCurrentState().displays.removeItem(existing.token());
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr uint32_t oldLayerStack = 0u;
- constexpr uint32_t newLayerStack = 123u;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStack state
- display.mutableDrawingDisplayState().layerStack = oldLayerStack;
- display.mutableCurrentDisplayState().layerStack = newLayerStack;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr ui::Rotation oldTransform = ui::ROTATION_0;
- constexpr ui::Rotation newTransform = ui::ROTATION_180;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the orientation state
- display.mutableDrawingDisplayState().orientation = oldTransform;
- display.mutableCurrentDisplayState().orientation = newTransform;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- const Rect oldLayerStackRect(0, 0, 0, 0);
- const Rect newLayerStackRect(0, 0, 123, 456);
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
- display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- const Rect oldFrame(0, 0, 0, 0);
- const Rect newFrame(0, 0, 123, 456);
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr int oldWidth = 0;
- constexpr int oldHeight = 10;
- constexpr int newWidth = 123;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new compositionengine::mock::DisplaySurface();
- sp<GraphicBuffer> buf = new GraphicBuffer();
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.setNativeWindow(nativeWindow);
- display.setDisplaySurface(displaySurface);
- // Setup injection expections
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().width = oldWidth;
- display.mutableDrawingDisplayState().height = oldHeight;
- display.mutableCurrentDisplayState().width = newWidth;
- display.mutableCurrentDisplayState().height = oldHeight;
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr int oldWidth = 0;
- constexpr int oldHeight = 10;
- constexpr int newHeight = 123;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new compositionengine::mock::DisplaySurface();
- sp<GraphicBuffer> buf = new GraphicBuffer();
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.setNativeWindow(nativeWindow);
- display.setDisplaySurface(displaySurface);
- // Setup injection expections
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().width = oldWidth;
- display.mutableDrawingDisplayState().height = oldHeight;
- display.mutableCurrentDisplayState().width = oldWidth;
- display.mutableCurrentDisplayState().height = newHeight;
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setDisplayStateLocked
- */
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
- // --------------------------------------------------------------------
- // Preconditions
-
- // We have an unknown display token not associated with a known display
- sp<BBinder> displayToken = new BBinder();
-
- // The requested display state references the unknown display.
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = displayToken;
- state.layerStack = 456;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The display token still doesn't match anything known.
- EXPECT_FALSE(hasCurrentDisplayState(displayToken));
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // No changes are made to the display
- DisplayState state;
- state.what = 0;
- state.token = display.token();
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a surface that can be set.
- sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
- // The current display state has the surface set
- display.mutableCurrentDisplayState().surface = surface;
-
- // The incoming request sets the same surface
- DisplayState state;
- state.what = DisplayState::eSurfaceChanged;
- state.token = display.token();
- state.surface = surface;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged.
- EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a surface that can be set.
- sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
- // The current display state does not have a surface
- display.mutableCurrentDisplayState().surface = nullptr;
-
- // The incoming request sets a surface
- DisplayState state;
- state.what = DisplayState::eSurfaceChanged;
- state.token = display.token();
- state.surface = surface;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display layer stack state is set to the new value
- EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 456u;
-
- // The incoming request sets the same layer stack
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456u;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 654u;
-
- // The incoming request sets a different layer stack
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456u;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The desired display state has been set to the new value.
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
- constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
- const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
- const Rect initialLayerStackRect = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state projection state is all set
- display.mutableCurrentDisplayState().orientation = initialOrientation;
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
- display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
-
- // The incoming request sets the same projection state
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientation = initialOrientation;
- state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
- state.layerStackSpaceRect = initialLayerStackRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
-
- EXPECT_EQ(initialOrientedDisplayRect,
- display.getCurrentDisplayState().orientedDisplaySpaceRect);
- EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
- constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state has an orientation set
- display.mutableCurrentDisplayState().orientation = initialOrientation;
-
- // The incoming request sets a different orientation
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientation = desiredOrientation;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
- using Case = SimplePrimaryDisplayCase;
- const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
- const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state does not have a orientedDisplaySpaceRect
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
-
- // The incoming request sets a orientedDisplaySpaceRect
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredOrientedDisplayRect,
- display.getCurrentDisplayState().orientedDisplaySpaceRect);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
- using Case = SimplePrimaryDisplayCase;
- const Rect initialLayerStackRect = {0, 0, 0, 0};
- const Rect desiredLayerStackRect = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state does not have a layerStackSpaceRect
- display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
-
- // The incoming request sets a layerStackSpaceRect
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.layerStackSpaceRect = desiredLayerStackRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialWidth = 1024;
- constexpr uint32_t initialHeight = 768;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state has a size set
- display.mutableCurrentDisplayState().width = initialWidth;
- display.mutableCurrentDisplayState().height = initialHeight;
-
- // The incoming request sets the same display size
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.width = initialWidth;
- state.height = initialHeight;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
- EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialWidth = 0;
- constexpr uint32_t desiredWidth = 1024;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display does not yet have a width
- display.mutableCurrentDisplayState().width = initialWidth;
-
- // The incoming request sets a display width
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.width = desiredWidth;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialHeight = 0;
- constexpr uint32_t desiredHeight = 768;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display does not yet have a height
- display.mutableCurrentDisplayState().height = initialHeight;
-
- // The incoming request sets a display height
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.height = desiredHeight;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::onInitializeDisplays
- */
-
-TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display is set up
- Case::Display::injectHwcDisplay(this);
- auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
- primaryDisplay.inject();
-
- // --------------------------------------------------------------------
- // 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);
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.onInitializeDisplays();
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The primary display should have a current state
- ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
- const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
- // The layer stack state should be set to zero
- EXPECT_EQ(0u, primaryDisplayState.layerStack);
- // The orientation state should be set to zero
- EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
- // The orientedDisplaySpaceRect state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
-
- // The layerStackSpaceRect state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
-
- // The width and height should both be zero
- EXPECT_EQ(0u, primaryDisplayState.width);
- EXPECT_EQ(0u, primaryDisplayState.height);
-
- // The display should be set to PowerMode::ON
- ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
- auto displayDevice = primaryDisplay.mutableDisplayDevice();
- EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
-
- // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
- FrameStats stats;
- mFlinger.getAnimFrameTracker().getStats(&stats);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // The compositor timing should be set to default values
- const auto& compositorTiming = mFlinger.getCompositorTiming();
- EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setPowerModeInternal
- */
-
-// Used when we simulate a display that supports doze.
-template <typename Display>
-struct DozeIsSupportedVariant {
- static constexpr bool DOZE_SUPPORTED = true;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
- IComposerClient::PowerMode::DOZE;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
- IComposerClient::PowerMode::DOZE_SUSPEND;
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(
- std::vector<DisplayCapability>({DisplayCapability::DOZE})),
- Return(Error::NONE)));
- }
-};
-
-template <typename Display>
-// Used when we simulate a display that does not support doze.
-struct DozeNotSupportedVariant {
- static constexpr bool DOZE_SUPPORTED = false;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
- IComposerClient::PowerMode::ON;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
- IComposerClient::PowerMode::ON;
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
- }
-};
-
-struct EventThreadBaseSupportedVariant {
- static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
- // The callback should not be notified to toggle VSYNC.
- EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
-
- // The event thread should not be notified.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
- }
-};
-
-struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
- }
-
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
- }
-};
-
-struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to enable VSYNC.
- EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
-
- // The event thread should be notified that the screen was acquired.
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
- }
-
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to disable VSYNC.
- EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
-
- // The event thread should not be notified that the screen was released.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
- }
-};
-
-struct DispSyncIsSupportedVariant {
- static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1);
- EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1);
- }
-
- static void setupEndResyncCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1);
- }
-};
-
-struct DispSyncNotSupportedVariant {
- static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-
- static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// There are a large number of transitions we could test, however we only test a
-// selected subset which provides complete test coverage of the implementation.
-// --------------------------------------------------------------------
-
-template <PowerMode initialPowerMode, PowerMode targetPowerMode>
-struct TransitionVariantCommon {
- static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
- static constexpr auto TARGET_POWER_MODE = targetPowerMode;
-
- static void verifyPostconditions(DisplayTransactionTest*) {}
-};
-
-struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
- Case::setupRepaintEverythingCallExpectations(test);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
- }
-};
-
-struct TransitionOffToDozeSuspendVariant
- : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupRepaintEverythingCallExpectations(test);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
- }
-};
-
-struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::DispSync::setupEndResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- }
-};
-
-struct TransitionDozeSuspendToOffVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- }
-};
-
-struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
- }
-};
-
-struct TransitionDozeSuspendToDozeVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
- }
-};
-
-struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- }
-};
-
-struct TransitionDozeSuspendToOnVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- }
-};
-
-struct TransitionOnToDozeSuspendVariant
- : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::DispSync::setupEndResyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- }
-};
-
-struct TransitionOnToUnknownVariant
- : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupNoComposerPowerModeCallExpectations(test);
- }
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// Rather than testing the cartesian product of of
-// DozeIsSupported/DozeNotSupported with all other options, we use one for one
-// display type, and the other for another display type.
-// --------------------------------------------------------------------
-
-template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
- typename DispSyncVariant, typename TransitionVariant>
-struct DisplayPowerCase {
- using Display = DisplayVariant;
- using Doze = DozeVariant;
- using EventThread = EventThreadVariant;
- using DispSync = DispSyncVariant;
- using Transition = TransitionVariant;
-
- static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
- Display::injectHwcDisplayWithNoDefaultCapabilities(test);
- auto display = Display::makeFakeExistingDisplayInjector(test);
- display.inject();
- display.mutableDisplayDevice()->setPowerMode(mode);
- return display;
- }
-
- static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
- test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
- }
-
- static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, invalidate()).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, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
- Return(Error::NONE)));
-
- // Any calls to get whether the display supports dozing will return the value set by the
- // policy variant.
- EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
-
- EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
- }
-
- static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
- }
-};
-
-// A sample configuration for the primary display.
-// In addition to having event thread support, we emulate doze support.
-template <typename TransitionVariant>
-using PrimaryDisplayPowerCase =
- DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
- EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
- TransitionVariant>;
-
-// A sample configuration for the external display.
-// In addition to not having event thread support, we emulate not having doze
-// support.
-template <typename TransitionVariant>
-using ExternalDisplayPowerCase =
- DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
- EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
- TransitionVariant>;
-
-class SetPowerModeInternalTest : public DisplayTransactionTest {
-public:
- template <typename Case>
- void transitionDisplayCommon();
-};
-
-template <PowerMode PowerMode>
-struct PowerModeInitialVSyncEnabled : public std::false_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
-
-template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- Case::Doze::setupComposerCallExpectations(this);
- auto display =
- Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
- Case::setInitialPrimaryHWVsyncEnabled(this,
- PowerModeInitialVSyncEnabled<
- Case::Transition::INITIAL_POWER_MODE>::value);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
- Case::Transition::template setupCallExpectations<Case>(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
- Case::Transition::TARGET_POWER_MODE);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- Case::Transition::verifyPostconditions(this);
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display device is set up
- Case::Display::injectHwcDisplay(this);
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display is already set to PowerMode::ON
- display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Insert display data so that the HWC thinks it created the virtual display.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-
- // A virtual display device is set up
- Case::Display::injectHwcDisplay(this);
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display is set to PowerMode::ON
- getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-} // namespace
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
new file mode 100644
index 0000000..01cdb28
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -0,0 +1,756 @@
+/*
+ * 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
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <type_traits>
+#include "DisplayIdentificationTest.h"
+
+#include <binder/IPCThreadState.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/mock/GraphicBufferConsumer.h>
+#include <gui/mock/GraphicBufferProducer.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DebugUtils.h>
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+#include "mock/MockNativeWindowSurface.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockSurfaceInterceptor.h"
+#include "mock/MockVsyncController.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+// TODO: Do not polute the android namespace
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+using hal::ColorMode;
+using hal::Connection;
+using hal::DisplayCapability;
+using hal::DisplayType;
+using hal::Error;
+using hal::Hdr;
+using hal::HWDisplayId;
+using hal::IComposer;
+using hal::IComposerClient;
+using hal::PerFrameMetadataKey;
+using hal::PowerMode;
+
+class DisplayTransactionTest : public testing::Test {
+public:
+ ~DisplayTransactionTest() override;
+
+ // --------------------------------------------------------------------
+ // Mock/Fake injection
+
+ void injectMockScheduler();
+ void injectMockComposer(int virtualDisplayCount);
+ void injectFakeBufferQueueFactory();
+ void injectFakeNativeWindowSurfaceFactory();
+ sp<DisplayDevice> injectDefaultInternalDisplay(
+ std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+
+ // --------------------------------------------------------------------
+ // Postcondition helpers
+
+ bool hasPhysicalHwcDisplay(hal::HWDisplayId hwcDisplayId);
+ bool hasTransactionFlagSet(int flag);
+ bool hasDisplayDevice(sp<IBinder> displayToken);
+ sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
+ bool hasCurrentDisplayState(sp<IBinder> displayToken);
+ const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
+ bool hasDrawingDisplayState(sp<IBinder> displayToken);
+ const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
+
+ // --------------------------------------------------------------------
+ // Test instances
+
+ TestableSurfaceFlinger mFlinger;
+ sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+ sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+ Hwc2::mock::PowerAdvisor mPowerAdvisor;
+
+ // 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;
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
+
+ mock::VsyncController* mVsyncController = new mock::VsyncController;
+ mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
+ mock::SchedulerCallback mSchedulerCallback;
+ mock::EventThread* mEventThread = new mock::EventThread;
+ mock::EventThread* mSFEventThread = new mock::EventThread;
+
+ // These mocks are created only when expected to be created via a factory.
+ sp<mock::GraphicBufferConsumer> mConsumer;
+ sp<mock::GraphicBufferProducer> mProducer;
+ surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
+
+protected:
+ DisplayTransactionTest();
+};
+
+constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
+constexpr int32_t DEFAULT_DPI = 320;
+constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
+
+constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
+
+/* ------------------------------------------------------------------------
+ * Boolean avoidance
+ *
+ * To make calls and template instantiations more readable, we define some
+ * local enums along with an implicit bool conversion.
+ */
+
+#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
+
+BOOL_SUBSTITUTE(Async);
+BOOL_SUBSTITUTE(Critical);
+BOOL_SUBSTITUTE(Primary);
+BOOL_SUBSTITUTE(Secure);
+BOOL_SUBSTITUTE(Virtual);
+
+template <typename PhysicalDisplay>
+struct PhysicalDisplayIdType {};
+
+template <uint64_t displayId>
+using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
+
+struct GpuVirtualDisplayIdType {};
+
+template <typename>
+struct IsPhysicalDisplayId : std::bool_constant<false> {};
+
+template <typename PhysicalDisplay>
+struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
+
+template <typename>
+struct DisplayIdGetter;
+
+template <typename PhysicalDisplay>
+struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static PhysicalDisplayId get() {
+ if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
+ ? LEGACY_DISPLAY_TYPE_PRIMARY
+ : LEGACY_DISPLAY_TYPE_EXTERNAL);
+ }
+
+ const auto info =
+ parseDisplayIdentificationData(PhysicalDisplay::PORT,
+ PhysicalDisplay::GET_IDENTIFICATION_DATA());
+ return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT);
+ }
+};
+
+template <uint64_t displayId>
+struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+ static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+};
+
+template <>
+struct DisplayIdGetter<GpuVirtualDisplayIdType> {
+ static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+};
+
+template <typename>
+struct DisplayConnectionTypeGetter {
+ static constexpr std::optional<DisplayConnectionType> value;
+};
+
+template <typename PhysicalDisplay>
+struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
+};
+
+template <typename>
+struct HwcDisplayIdGetter {
+ static constexpr std::optional<HWDisplayId> value;
+};
+
+constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <uint64_t displayId>
+struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+ static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
+// DisplayIdType can be:
+// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
+// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
+// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
+template <typename DisplayIdType, int width, int height, Critical critical, Async async,
+ Secure secure, Primary primary, int grallocUsage>
+struct DisplayVariant {
+ using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+ using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+ using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
+
+ // The display width and height
+ static constexpr int WIDTH = width;
+ static constexpr int HEIGHT = height;
+
+ static constexpr int GRALLOC_USAGE = grallocUsage;
+
+ // Whether the display is virtual or physical
+ static constexpr Virtual VIRTUAL =
+ IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
+
+ // When creating native window surfaces for the framebuffer, whether those should be critical
+ static constexpr Critical CRITICAL = critical;
+
+ // When creating native window surfaces for the framebuffer, whether those should be async
+ static constexpr Async ASYNC = async;
+
+ // Whether the display should be treated as secure
+ static constexpr Secure SECURE = secure;
+
+ // Whether the display is primary
+ static constexpr Primary PRIMARY = primary;
+
+ static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+ if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
+ ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
+ } else {
+ // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
+ // from calling into HWComposer. This way all virtual displays will get
+ // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
+ // In this case we later override it by calling display.setDisplayIdForTesting().
+ ceDisplayArgs.setUseHwcVirtualDisplays(false);
+
+ GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
+ .value_or(GpuVirtualDisplayId(0));
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(desiredDisplayId));
+
+ auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
+ ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
+ }
+ ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs.build());
+
+ if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
+ // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
+ // override it with the correct HalVirtualDisplayId.
+ compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
+ }
+
+ auto injector =
+ TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
+ compositionDisplay,
+ CONNECTION_TYPE::value,
+ HWC_DISPLAY_ID_OPT::value,
+ static_cast<bool>(PRIMARY));
+
+ injector.setSecure(static_cast<bool>(SECURE));
+ injector.setNativeWindow(test->mNativeWindow);
+
+ // Creating a DisplayDevice requires getting default dimensions from the
+ // native window along with some other initial setup.
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+
+ return injector;
+ }
+
+ // Called by tests to set up any native window creation call expectations.
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
+ .WillOnce(Return(test->mNativeWindow));
+
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+ }
+
+ static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
+ .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
+ .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
+ .WillRepeatedly(Return(NO_ERROR));
+ }
+
+ static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
+ }
+};
+
+template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
+ typename PhysicalDisplay = void>
+struct HwcDisplayVariant {
+ // The display id supplied by the HWC
+ static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
+
+ // The HWC display type
+ static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
+
+ // The HWC active configuration id
+ static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
+ static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
+
+ static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
+ test->mFlinger.mutablePendingHotplugEvents().emplace_back(
+ TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection});
+ }
+
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
+ const auto displayId = DisplayVariant::DISPLAY_ID::get();
+ ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+ static_cast<bool>(DisplayVariant::PRIMARY))
+ .setHwcDisplayId(HWC_DISPLAY_ID)
+ .setWidth(DisplayVariant::WIDTH)
+ .setHeight(DisplayVariant::HEIGHT)
+ .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
+ .setPowerMode(INIT_POWER_MODE)
+ .inject(&test->mFlinger, test->mComposer);
+ }
+
+ // Called by tests to inject a HWC display setup
+ 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);
+ }
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPhysical({DisplayVariant::DISPLAY_ID::get(),
+ PhysicalDisplay::CONNECTION_TYPE})
+ .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+ .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
+ static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
+ constexpr auto CONNECTION_TYPE =
+ PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
+ ? IComposerClient::DisplayConnectionType::INTERNAL
+ : IComposerClient::DisplayConnectionType::EXTERNAL;
+
+ EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
+ .WillOnce(Return(hal::Error::NONE));
+ EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::DPI_X, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::DPI_Y, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+
+ if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
+ SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(Return(Error::UNSUPPORTED));
+ }
+ }
+
+ // Called by tests to set up HWC call expectations
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
+ }
+};
+
+// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
+constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+
+template <typename PhysicalDisplay, int width, int height, Critical critical>
+struct PhysicalDisplayVariant
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
+ critical, Async::FALSE, Secure::TRUE,
+ PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ PhysicalDisplay> {};
+
+template <bool hasIdentificationData>
+struct PrimaryDisplay {
+ static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
+ static constexpr Primary PRIMARY = Primary::TRUE;
+ static constexpr uint8_t PORT = 255;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
+};
+
+template <bool hasIdentificationData>
+struct ExternalDisplay {
+ static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 254;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+struct TertiaryDisplay {
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 253;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+// A primary display is a physical display that is critical
+using PrimaryDisplayVariant =
+ PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+
+// An external display is physical display that is not critical.
+using ExternalDisplayVariant =
+ PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+
+// A virtual display not supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
+
+template <int width, int height, Secure secure>
+struct NonHwcVirtualDisplayVariant
+ : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
+ using Base =
+ DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+
+ static void injectHwcDisplay(DisplayTransactionTest*) {}
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(Base::DISPLAY_ID::get()));
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .setGpuVirtualDisplayIdGenerator(
+ test->mFlinger.gpuVirtualDisplayIdGenerator())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
+ }
+
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+ EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+ }
+};
+
+// A virtual display supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
+
+template <int width, int height, Secure secure>
+struct HwcVirtualDisplayVariant
+ : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+ HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
+ Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+ using Self = HwcVirtualDisplayVariant<width, height, secure>;
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ // In order to prevent compostition engine calling into HWComposer, we
+ // 1. turn off the use of HWC virtual displays,
+ // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
+ // 3. override the ID by calling setDisplayIdForTesting()
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(GpuVirtualDisplayId(0)));
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(false)
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .setGpuVirtualDisplayIdGenerator(
+ test->mFlinger.gpuVirtualDisplayIdGenerator())
+ .build();
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ if (const auto displayId = Base::DISPLAY_ID::get();
+ HalVirtualDisplayId::tryCast(displayId)) {
+ test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ }
+
+ return compositionDisplay;
+ }
+
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+ EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+ }
+
+ static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
+ }
+};
+
+// For this variant, the display is not a HWC display, so no HDR support should
+// be configured.
+struct NonHwcDisplayHdrSupportVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
+ }
+};
+
+// For this variant, the composer should respond with am empty list of HDR
+// modes, so no HDR support should be configured.
+template <typename Display>
+struct HdrNotSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
+ }
+};
+
+struct NonHwcPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
+ }
+};
+
+template <typename Display>
+struct NoPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
+ }
+};
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, but the display should respond with an empty list of supported color
+// modes. Wide-color support for the display should not be configured.
+template <typename Display>
+struct WideColorNotSupportedVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
+ test->mFlinger.mutableHasWideColorDisplay() = 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);
+ }
+};
+
+// For this variant, SurfaceFlinger should not configure itself with wide
+// display support, so the display should not be configured for wide-color
+// support.
+struct WideColorSupportNotConfiguredVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableHasWideColorDisplay() = false;
+ test->mFlinger.mutableUseColorManagement() = 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);
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Typical display configurations to test
+ */
+
+template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
+ typename PerFrameMetadataSupportPolicy>
+struct Case {
+ using Display = DisplayPolicy;
+ using WideColorSupport = WideColorSupportPolicy;
+ using HdrSupport = HdrSupportPolicy;
+ using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
+};
+
+using SimplePrimaryDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using SimpleExternalDisplayCase =
+ Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
+ HdrNotSupportedVariant<ExternalDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
+using SimpleTertiaryDisplayCase =
+ Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
+ HdrNotSupportedVariant<TertiaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
+
+using NonHwcVirtualDisplayCase =
+ Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
+ WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
+ NonHwcPerFrameMetadataSupportVariant>;
+using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
+using HwcVirtualDisplayCase =
+ Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
+ HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index aab6d01..f680bdb 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -46,7 +46,9 @@
MOCK_METHOD1(setVSyncEnabled, void(bool));
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
- MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+ MOCK_METHOD2(setDuration,
+ void(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration));
MOCK_METHOD1(pauseVsyncCallback, void(bool));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
@@ -74,7 +76,8 @@
ISurfaceComposer::ConfigChanged configChanged);
void expectVSyncSetEnabledCallReceived(bool expectedState);
- void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+ void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
+ std::chrono::nanoseconds expectedReadyDuration);
VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
void expectInterceptCallReceived(nsecs_t expectedTimestamp);
void expectVsyncEventReceivedByConnection(const char* name,
@@ -89,7 +92,8 @@
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
- AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+ AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
+ mVSyncSetDurationCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
@@ -114,8 +118,8 @@
EXPECT_CALL(*mVSyncSource, setCallback(_))
.WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
- EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
- .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+ EXPECT_CALL(*mVSyncSource, setDuration(_, _))
+ .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable()));
createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
@@ -137,6 +141,7 @@
void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
mThread = std::make_unique<impl::EventThread>(std::move(source),
+ /*tokenManager=*/nullptr,
mInterceptVSyncCallRecorder.getInvocable());
// EventThread should register itself as VSyncSource callback.
@@ -159,10 +164,12 @@
EXPECT_EQ(expectedState, std::get<0>(args.value()));
}
-void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) {
- auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall();
+void EventThreadTest::expectVSyncSetDurationCallReceived(
+ std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) {
+ auto args = mVSyncSetDurationCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
- EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
+ EXPECT_EQ(expectedDuration, std::get<0>(args.value()));
+ EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value()));
}
VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
@@ -229,7 +236,7 @@
TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
- EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.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());
@@ -258,14 +265,14 @@
// Use the received callback to signal a first vsync event.
// The interceptor should receive the event, as well as the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
// The interceptor should receive the event, but the the connection should
// not as it was only interested in the first.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -299,7 +306,7 @@
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the second connection. The first connection should not
// get the event.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 0);
expectInterceptCallReceived(123);
EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -314,17 +321,17 @@
// Send a vsync event. EventThread should then make a call to the
// interceptor, and the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
- mCallback->onVSyncEvent(789, 777);
+ mCallback->onVSyncEvent(789, 777, 111);
expectInterceptCallReceived(789);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -336,22 +343,22 @@
expectVSyncSetEnabledCallReceived(true);
// The first event will be seen by the interceptor, and not the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
// The third event will be seen by the interceptor, and not the connection.
- mCallback->onVSyncEvent(789, 777);
+ mCallback->onVSyncEvent(789, 777, 744);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
- mCallback->onVSyncEvent(101112, 7847);
+ mCallback->onVSyncEvent(101112, 7847, 86);
expectInterceptCallReceived(101112);
expectVsyncEventReceivedByConnection(101112, 4u);
}
@@ -366,7 +373,7 @@
mConnection = nullptr;
// The first event will be seen by the interceptor, and not the connection.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
@@ -386,13 +393,13 @@
// The first event will be seen by the interceptor, and by the connection,
// which then returns an error.
- mCallback->onVSyncEvent(123, 456);
+ 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.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
@@ -420,7 +427,7 @@
// The first event will be seen by the interceptor, and by the connection,
// which then returns an error.
- mCallback->onVSyncEvent(123, 456);
+ mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
@@ -440,13 +447,13 @@
// The first event will be seen by the interceptor, and by the connection,
// which then returns an non-fatal error.
- mCallback->onVSyncEvent(123, 456);
+ 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.
- mCallback->onVSyncEvent(456, 123);
+ mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
@@ -455,8 +462,8 @@
}
TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
- mThread->setPhaseOffset(321);
- expectVSyncSetPhaseOffsetCallReceived(321);
+ mThread->setDuration(321ns, 456ns);
+ expectVSyncSetDurationCallReceived(321ns, 456ns);
}
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
new file mode 100644
index 0000000..69efd7f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -0,0 +1,284 @@
+/*
+ * 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 <FrameTimeline/FrameTimeline.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <cinttypes>
+
+using namespace std::chrono_literals;
+
+namespace android::frametimeline {
+
+class FrameTimelineTest : public testing::Test {
+public:
+ FrameTimelineTest() {
+ 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());
+ }
+
+ ~FrameTimelineTest() {
+ 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());
+ }
+
+ void SetUp() override {
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>();
+ mTokenManager = &mFrameTimeline->mTokenManager;
+ maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
+ maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
+ }
+
+ void flushTokens(nsecs_t flushTime) {
+ std::lock_guard<std::mutex> lock(mTokenManager->mMutex);
+ mTokenManager->flushTokens(flushTime);
+ }
+
+ SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]);
+ }
+
+ std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return mFrameTimeline->mDisplayFrames[idx];
+ }
+
+ static bool compareTimelineItems(const TimelineItem& a, const TimelineItem& b) {
+ return a.startTime == b.startTime && a.endTime == b.endTime &&
+ a.presentTime == b.presentTime;
+ }
+
+ const std::unordered_map<int64_t, TimelineItem>& getPredictions() {
+ return mTokenManager->mPredictions;
+ }
+
+ uint32_t getNumberOfDisplayFrames() {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
+ }
+
+ std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
+ impl::TokenManager* mTokenManager;
+ FenceToFenceTimeMap fenceFactory;
+ uint32_t* maxDisplayFrames;
+ nsecs_t maxTokenRetentionTime;
+};
+
+TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
+ EXPECT_EQ(getPredictions().size(), 1);
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1);
+
+ // token1 should have expired
+ EXPECT_EQ(getPredictions().size(), 1);
+ EXPECT_EQ(predictions.has_value(), false);
+
+ predictions = mTokenManager->getPredictionsForToken(token2);
+ EXPECT_EQ(compareTimelineItems(*predictions, TimelineItem(10, 20, 30)), true);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
+ flushTokens(systemTime() + maxTokenRetentionTime);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+
+ EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
+}
+
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+
+ EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
+ EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+
+ // Set up the display frame
+ mFrameTimeline->setSfWakeUp(token1, 20);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfPresent(25, presentFence1);
+ presentFence1->signalForTest(30);
+
+ // Trigger a flush by calling setSfPresent for the next frame
+ mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfPresent(55, presentFence2);
+
+ auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
+ EXPECT_EQ(droppedSurfaceFrame.getPresentState(), SurfaceFrame::PresentState::Dropped);
+ EXPECT_EQ(droppedSurfaceFrame.getActuals().presentTime, 0);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken1);
+ auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken("layer2", surfaceFrameToken1);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame2),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+ presentFence1->signalForTest(42);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame3 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken2);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
+ EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
+ EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
+}
+
+TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
+ // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
+ int frameTimeFactor = 0;
+ for (size_t i = 0; i < *maxDisplayFrames; i++) {
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
+ {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
+ int64_t sfToken = mTokenManager->generateTokenForPredictions(
+ {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
+ presentFence->signalForTest(32 + frameTimeFactor);
+ frameTimeFactor += 30;
+ }
+ auto displayFrame0 = getDisplayFrame(0);
+
+ // The 0th Display Frame should have actuals 22, 27, 32
+ EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)),
+ true);
+
+ // Add one more display frame
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
+ {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
+ int64_t sfToken = mTokenManager->generateTokenForPredictions(
+ {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
+ presentFence->signalForTest(32 + frameTimeFactor);
+ displayFrame0 = getDisplayFrame(0);
+
+ // The window should have slided by 1 now and the previous 0th display frame
+ // should have been removed from the deque
+ EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)),
+ true);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+ surfaceFrame->setActualQueueTime(123);
+ surfaceFrame->setAcquireFenceTime(456);
+ EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
+}
+
+TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+ surfaceFrame->setActualQueueTime(456);
+ surfaceFrame->setAcquireFenceTime(123);
+ EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
+}
+
+TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ presentFence->signalForTest(2);
+
+ // Size shouldn't exceed maxDisplayFrames - 64
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+ // Increase the size to 256
+ mFrameTimeline->setMaxDisplayFrames(256);
+ EXPECT_EQ(*maxDisplayFrames, 256);
+
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+ // Shrink the size to 128
+ mFrameTimeline->setMaxDisplayFrames(128);
+ EXPECT_EQ(*maxDisplayFrames, 128);
+
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+}
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
new file mode 100644
index 0000000..53dfe3f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrameTimeline.h"
+#include "Scheduler/MessageQueue.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using namespace std::chrono_literals;
+using namespace testing;
+
+using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
+
+class TestableMessageQueue : public impl::MessageQueue {
+public:
+ class MockHandler : public MessageQueue::Handler {
+ public:
+ explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
+ ~MockHandler() override = default;
+ MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+ };
+
+ TestableMessageQueue() = default;
+ ~TestableMessageQueue() override = default;
+
+ void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+
+ void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+ vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
+ }
+};
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+ MockVSyncDispatch() = default;
+ ~MockVSyncDispatch() override = default;
+
+ MOCK_METHOD2(registerCallback,
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
+ MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+class MockTokenManager : public frametimeline::TokenManager {
+public:
+ MockTokenManager() = default;
+ ~MockTokenManager() override = default;
+
+ MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+};
+
+class MessageQueueTest : public testing::Test {
+public:
+ MessageQueueTest() = default;
+ ~MessageQueueTest() override = default;
+
+ void SetUp() override {
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
+
+ EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
+ EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ sp<TestableMessageQueue::MockHandler> mHandler =
+ new TestableMessageQueue::MockHandler(mEventQueue);
+ MockVSyncDispatch mVSyncDispatch;
+ MockTokenManager mTokenManager;
+ TestableMessageQueue mEventQueue;
+
+ const CallbackToken mCallbackToken{5};
+ constexpr static auto mDuration = std::chrono::nanoseconds(100ms);
+ constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms);
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(MessageQueueTest, invalidate) {
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwice) {
+ InSequence s;
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+ InSequence s;
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+ const auto startTime = 100;
+ const auto endTime = startTime + mDuration.count();
+ const auto presentTime = 500;
+ const auto vsyncId = 42;
+ EXPECT_CALL(mTokenManager,
+ generateTokenForPredictions(
+ frametimeline::TimelineItem(startTime, endTime, presentTime)))
+ .WillOnce(Return(vsyncId));
+ EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+
+ const auto timingAfterCallback =
+ scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = presentTime};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
+
+ const auto timing =
+ scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index f6bf05a..2c8178e 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -14,10 +14,6 @@
* 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 "LibSurfaceFlingerUnittests"
@@ -31,8 +27,8 @@
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -64,7 +60,7 @@
static constexpr int32_t PRIORITY_UNSET = -1;
void setupScheduler();
- void setupComposer(int virtualDisplayCount);
+ void setupComposer(uint32_t virtualDisplayCount);
sp<BufferQueueLayer> createBufferQueueLayer();
sp<BufferStateLayer> createBufferStateLayer();
sp<EffectLayer> createEffectLayer();
@@ -122,7 +118,11 @@
}
void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
- layer->commitTransaction(layer->getCurrentState());
+ layer->pushPendingState();
+ auto c = layer->getCurrentState();
+ if (layer->applyPendingStates(&c)) {
+ layer->commitTransaction(c);
+ }
}
void RefreshRateSelectionTest::setupScheduler() {
@@ -139,17 +139,18 @@
.WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
- auto primaryDispSync = std::make_unique<mock::DispSync>();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
- EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*primaryDispSync, getPeriod())
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
- std::move(sfEventThread));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
}
-void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+void RefreshRateSelectionTest::setupComposer(uint32_t virtualDisplayCount) {
mComposer = new Hwc2::mock::Composer();
EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
@@ -281,6 +282,3 @@
} // namespace
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 39e793a..eee9400 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -120,8 +120,8 @@
mScheduler.dump(handle, output);
EXPECT_TRUE(output.empty());
- EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
- mScheduler.setPhaseOffset(handle, 10);
+ EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0);
+ mScheduler.setDuration(handle, 10ns, 20ns);
}
TEST_F(SchedulerTest, validConnectionHandle) {
@@ -146,8 +146,8 @@
mScheduler.dump(mConnectionHandle, output);
EXPECT_FALSE(output.empty());
- EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
- mScheduler.setPhaseOffset(mConnectionHandle, 10);
+ EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1);
+ mScheduler.setDuration(mConnectionHandle, 10ns, 20ns);
static constexpr size_t kEventConnections = 5;
EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections));
@@ -175,4 +175,25 @@
mScheduler.chooseRefreshRateForContent();
}
+TEST_F(SchedulerTest, testDispatchCachedReportedConfig) {
+ // If the optional fields are cleared, the function should return before
+ // onConfigChange is called.
+ mScheduler.clearOptionalFieldsInFeatures();
+ EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedConfig());
+ EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+}
+
+TEST_F(SchedulerTest, onNonPrimaryDisplayConfigChanged_invalidParameters) {
+ HwcConfigIndexType configId = HwcConfigIndexType(111);
+ nsecs_t vsyncPeriod = 111111;
+
+ // If the handle is incorrect, the function should return before
+ // onConfigChange is called.
+ Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+ EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayConfigChanged(invalidHandle,
+ PHYSICAL_DISPLAY_ID,
+ configId, vsyncPeriod));
+ EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6c01f85..efee826 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -32,9 +32,9 @@
#pragma clang diagnostic pop // ignored "-Wconversion"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -157,7 +157,11 @@
void SetFrameRateTest::commitTransaction() {
for (auto layer : mLayers) {
- layer.get()->commitTransaction(layer.get()->getCurrentState());
+ layer->pushPendingState();
+ auto c = layer->getCurrentState();
+ if (layer->applyPendingStates(&c)) {
+ layer->commitTransaction(c);
+ }
}
}
@@ -175,14 +179,15 @@
.WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
- auto primaryDispSync = std::make_unique<mock::DispSync>();
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
- EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*primaryDispSync, getPeriod())
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
- mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
- std::move(sfEventThread));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
}
void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
new file mode 100644
index 0000000..2362a31
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 {
+
+class CreateDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
+ const String8 name("virtual.test");
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_FALSE(display.isSecure);
+ EXPECT_EQ(name.string(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
+ const String8 name("virtual.test");
+
+ // --------------------------------------------------------------------
+ // 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();
+ // Set the calling identity to graphics so captureDisplay with secure is allowed.
+ IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 |
+ AID_GRAPHICS);
+ sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
+ IPCThreadState::self()->restoreCallingIdentity(oldId);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_TRUE(display.isSecure);
+ EXPECT_EQ(name.string(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
new file mode 100644
index 0000000..0614434
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 {
+
+class DestroyDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A virtual display exists
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.destroyDisplay(existing.token());
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been removed from the current state
+ EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
+
+ // Ths display should still exist in the drawing state
+ EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
+
+ // The display transaction needed flasg should be set
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+}
+
+TEST_F(DestroyDisplayTest, destroyDisplayHandlesUnknownDisplay) {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ sp<BBinder> displayToken = new BBinder();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.destroyDisplay(displayToken);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
new file mode 100644
index 0000000..0171f1b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+ GetDisplayNativePrimaries();
+ void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+ void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+ static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+ SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+ ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ primaries.red.X = startingVal++;
+ primaries.red.Y = startingVal++;
+ primaries.red.Z = startingVal++;
+ primaries.green.X = startingVal++;
+ primaries.green.Y = startingVal++;
+ primaries.green.Z = startingVal++;
+ primaries.blue.X = startingVal++;
+ primaries.blue.Y = startingVal++;
+ primaries.blue.Z = startingVal++;
+ primaries.white.X = startingVal++;
+ primaries.white.Y = startingVal++;
+ primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+ const ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ EXPECT_EQ(primaries.red.X, startingVal++);
+ EXPECT_EQ(primaries.red.Y, startingVal++);
+ EXPECT_EQ(primaries.red.Z, startingVal++);
+ EXPECT_EQ(primaries.green.X, startingVal++);
+ EXPECT_EQ(primaries.green.Y, startingVal++);
+ EXPECT_EQ(primaries.green.Z, startingVal++);
+ EXPECT_EQ(primaries.blue.X, startingVal++);
+ EXPECT_EQ(primaries.blue.Y, startingVal++);
+ EXPECT_EQ(primaries.blue.Z, startingVal++);
+ EXPECT_EQ(primaries.white.X, startingVal++);
+ EXPECT_EQ(primaries.white.Y, startingVal++);
+ EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+ auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+ injector.inject();
+ auto internalDisplayToken = injector.token();
+
+ ui::DisplayPrimaries expectedPrimaries;
+ populateDummyDisplayNativePrimaries(expectedPrimaries);
+ mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+ sp<BBinder> notInternalDisplayToken = new BBinder();
+
+ ui::DisplayPrimaries primaries;
+ populateDummyDisplayNativePrimaries(primaries);
+ EXPECT_EQ(NAME_NOT_FOUND,
+ mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+ // Check primaries argument wasn't modified in case of failure
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
new file mode 100644
index 0000000..cd3f6ab
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -0,0 +1,734 @@
+/*
+ * 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 {
+
+class HandleTransactionLockedTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void setupCommonPreconditions();
+
+ template <typename Case, bool connected>
+ static void expectHotplugReceived(mock::EventThread*);
+
+ template <typename Case>
+ void setupCommonCallExpectationsForConnectProcessing();
+
+ template <typename Case>
+ void setupCommonCallExpectationsForDisconnectProcessing();
+
+ template <typename Case>
+ void processesHotplugConnectCommon();
+
+ template <typename Case>
+ void ignoresHotplugConnectCommon();
+
+ template <typename Case>
+ void processesHotplugDisconnectCommon();
+
+ template <typename Case>
+ void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
+
+ template <typename Case>
+ void verifyPhysicalDisplayIsConnected();
+
+ void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
+};
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonPreconditions() {
+ // Wide color displays support is configured appropriately
+ Case::WideColorSupport::injectConfigChange(this);
+
+ // SurfaceFlinger will use a test-controlled factory for BufferQueues
+ injectFakeBufferQueueFactory();
+
+ // SurfaceFlinger will use a test-controlled factory for native window
+ // surfaces.
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+template <typename Case, bool connected>
+void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+ const auto convert = [](auto physicalDisplayId) {
+ return std::make_optional(DisplayId{physicalDisplayId});
+ };
+
+ EXPECT_CALL(*eventThread,
+ onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
+ .Times(1);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+ Case::Display::setupHwcHotplugCallExpectations(this);
+
+ Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ 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 HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+ expectHotplugReceived<Case, false>(mEventThread);
+ expectHotplugReceived<Case, false>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+ // The display device should have been set up in the list of displays.
+ ASSERT_TRUE(hasDisplayDevice(displayToken));
+ const auto& device = getDisplayDevice(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+
+ std::optional<DisplayDeviceState::Physical> expectedPhysical;
+ if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ expectedPhysical = {.id = *displayId,
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
+ }
+
+ // The display should have been set up in the current display state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& current = getCurrentDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
+ EXPECT_EQ(expectedPhysical, current.physical);
+
+ // The display should have been set up in the drawing display state
+ ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+ const auto& draw = getDrawingDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+ EXPECT_EQ(expectedPhysical, draw.physical);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+ // HWComposer should have an entry for the display
+ EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+ auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
+
+ verifyDisplayIsConnected<Case>(displayToken);
+}
+
+void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+ EXPECT_FALSE(hasDisplayDevice(displayToken));
+ EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+ EXPECT_FALSE(hasDrawingDisplayState(displayToken));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ verifyPhysicalDisplayIsConnected<Case>();
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
+ .Times(0);
+
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+ processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest,
+ processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
+ // Inject an external display.
+ ExternalDisplayVariant::injectHwcDisplay(this);
+
+ processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+ // Inject a primary display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+
+ processesHotplugConnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+ // Inject both a primary and external display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+ ExternalDisplayVariant::injectHwcDisplay(this);
+
+ // TODO: This is an unnecessary call.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
+ SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+
+ ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
+ processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+ processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ // A hotplug disconnect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ // A hotplug connect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+ EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+
+ // A new display should be connected in its place
+
+ verifyPhysicalDisplayIsConnected<Case>();
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // The HWC supports at least one virtual display
+ injectMockComposer(1);
+
+ setupCommonPreconditions<Case>();
+
+ // A virtual display was added to the current state, and it has a
+ // surface(producer)
+ sp<BBinder> displayToken = new BBinder();
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
+ state.surface = surface;
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
+
+ EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
+
+ EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
+ EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
+
+ Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display device should have been set up in the list of displays.
+ verifyDisplayIsConnected<Case>(displayToken);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // Cleanup
+ mFlinger.mutableCurrentState().displays.removeItem(displayToken);
+ mFlinger.mutableDrawingState().displays.removeItem(displayToken);
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // The HWC supports at least one virtual display
+ injectMockComposer(1);
+
+ setupCommonPreconditions<Case>();
+
+ // A virtual display was added to the current state, but it does not have a
+ // surface.
+ sp<BBinder> displayToken = new BBinder();
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // There will not be a display device set up.
+ EXPECT_FALSE(hasDisplayDevice(displayToken));
+
+ // The drawing display state will be set from the current display state.
+ ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+ const auto& draw = getDrawingDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A virtual display is set up but is removed from the current state.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+ mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+ mFlinger.mutableCurrentState().displays.removeItem(existing.token());
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr uint32_t oldLayerStack = 0u;
+ constexpr uint32_t newLayerStack = 123u;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStack state
+ display.mutableDrawingDisplayState().layerStack = oldLayerStack;
+ display.mutableCurrentDisplayState().layerStack = newLayerStack;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr ui::Rotation oldTransform = ui::ROTATION_0;
+ constexpr ui::Rotation newTransform = ui::ROTATION_180;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the orientation state
+ display.mutableDrawingDisplayState().orientation = oldTransform;
+ display.mutableCurrentDisplayState().orientation = newTransform;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ const Rect oldLayerStackRect(0, 0, 0, 0);
+ const Rect newLayerStackRect(0, 0, 123, 456);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ const Rect oldFrame(0, 0, 0, 0);
+ const Rect newFrame(0, 0, 123, 456);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr int oldWidth = 0;
+ constexpr int oldHeight = 10;
+ constexpr int newWidth = 123;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = oldWidth;
+ display.mutableDrawingDisplayState().height = oldHeight;
+ display.mutableCurrentDisplayState().width = newWidth;
+ display.mutableCurrentDisplayState().height = oldHeight;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr int oldWidth = 0;
+ constexpr int oldHeight = 10;
+ constexpr int newHeight = 123;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = oldWidth;
+ display.mutableDrawingDisplayState().height = oldHeight;
+ display.mutableCurrentDisplayState().width = oldWidth;
+ display.mutableCurrentDisplayState().height = newHeight;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
new file mode 100644
index 0000000..69e0501
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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"
+
+#include <android/hardware/power/Boost.h>
+
+namespace android {
+namespace {
+
+using android::hardware::power::Boost;
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ 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
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+ std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
new file mode 100644
index 0000000..42f4cf3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 {
+
+class OnHotplugReceivedTest : public DisplayTransactionTest {};
+
+TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) {
+ constexpr int currentSequenceId = 123;
+ constexpr HWDisplayId hwcDisplayId1 = 456;
+ constexpr HWDisplayId hwcDisplayId2 = 654;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does not appear to be
+ // the main thread.
+ mFlinger.mutableMainThreadId() = std::thread::id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Simulate two hotplug events (a connect and a disconnect)
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // All events should be in the pending event queue.
+ const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
+ ASSERT_EQ(2u, pendingEvents.size());
+ EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
+ EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
+ EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
+ EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugDiscardsUnexpectedEvents) {
+ constexpr int currentSequenceId = 123;
+ constexpr int otherSequenceId = 321;
+ constexpr HWDisplayId displayId = 456;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does not appear to be
+ // the main thread.
+ mFlinger.mutableMainThreadId() = std::thread::id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We do not expect any calls to invalidate().
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Call with an unexpected sequence id
+ mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should not be set
+ EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // There should be no pending events
+ EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
+ constexpr int currentSequenceId = 123;
+ constexpr HWDisplayId displayId1 = 456;
+
+ // --------------------------------------------------------------------
+ // Note:
+ // --------------------------------------------------------------------
+ // This test case is a bit tricky. We want to verify that
+ // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
+ // don't really want to provide coverage for everything the later function
+ // does as there are specific tests for it.
+ // --------------------------------------------------------------------
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does appear to be the
+ // main thread.
+ mFlinger.mutableMainThreadId() = std::this_thread::get_id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Simulate a disconnect on a display id that is not connected. This should
+ // be enqueued by onHotplugReceived(), and dequeued by
+ // processDisplayHotplugEventsLocked(), but then ignored as invalid.
+ mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // There should be no event queued on return, as it should have been
+ // processed.
+ EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
new file mode 100644
index 0000000..7a9403b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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 {
+
+class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+
+TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display is set up
+ Case::Display::injectHwcDisplay(this);
+ auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
+ primaryDisplay.inject();
+
+ // --------------------------------------------------------------------
+ // 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);
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.onInitializeDisplays();
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should have a current state
+ ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
+ const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
+ // The layer stack state should be set to zero
+ EXPECT_EQ(0u, primaryDisplayState.layerStack);
+ // The orientation state should be set to zero
+ EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
+
+ // The orientedDisplaySpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
+
+ // The layerStackSpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
+
+ // The width and height should both be zero
+ EXPECT_EQ(0u, primaryDisplayState.width);
+ EXPECT_EQ(0u, primaryDisplayState.height);
+
+ // The display should be set to PowerMode::ON
+ ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
+ auto displayDevice = primaryDisplay.mutableDisplayDevice();
+ EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
+
+ // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
+ FrameStats stats;
+ mFlinger.getAnimFrameTracker().getStats(&stats);
+ EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // The compositor timing should be set to default values
+ const auto& compositorTiming = mFlinger.getCompositorTiming();
+ EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
+ EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
+ EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
new file mode 100644
index 0000000..be01984
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -0,0 +1,493 @@
+/*
+ * 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"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class SetDisplayStateLockedTest : public DisplayTransactionTest {};
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // We have an unknown display token not associated with a known display
+ sp<BBinder> displayToken = new BBinder();
+
+ // The requested display state references the unknown display.
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = displayToken;
+ state.layerStack = 456;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The display token still doesn't match anything known.
+ EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // No changes are made to the display
+ DisplayState state;
+ state.what = 0;
+ state.token = display.token();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a surface that can be set.
+ sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+ // The current display state has the surface set
+ display.mutableCurrentDisplayState().surface = surface;
+
+ // The incoming request sets the same surface
+ DisplayState state;
+ state.what = DisplayState::eSurfaceChanged;
+ state.token = display.token();
+ state.surface = surface;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged.
+ EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a surface that can be set.
+ sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+ // The current display state does not have a surface
+ display.mutableCurrentDisplayState().surface = nullptr;
+
+ // The incoming request sets a surface
+ DisplayState state;
+ state.what = DisplayState::eSurfaceChanged;
+ state.token = display.token();
+ state.surface = surface;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display layer stack state is set to the new value
+ EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display has a layer stack set
+ display.mutableCurrentDisplayState().layerStack = 456u;
+
+ // The incoming request sets the same layer stack
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = display.token();
+ state.layerStack = 456u;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display has a layer stack set
+ display.mutableCurrentDisplayState().layerStack = 654u;
+
+ // The incoming request sets a different layer stack
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = display.token();
+ state.layerStack = 456u;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The desired display state has been set to the new value.
+ EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
+ const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+ const Rect initialLayerStackRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state projection state is all set
+ display.mutableCurrentDisplayState().orientation = initialOrientation;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+ // The incoming request sets the same projection state
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientation = initialOrientation;
+ state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ state.layerStackSpaceRect = initialLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
+
+ EXPECT_EQ(initialOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+ EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
+ constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state has an orientation set
+ display.mutableCurrentDisplayState().orientation = initialOrientation;
+
+ // The incoming request sets a different orientation
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientation = desiredOrientation;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+ const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state does not have a orientedDisplaySpaceRect
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+
+ // The incoming request sets a orientedDisplaySpaceRect
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ const Rect initialLayerStackRect = {0, 0, 0, 0};
+ const Rect desiredLayerStackRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state does not have a layerStackSpaceRect
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+ // The incoming request sets a layerStackSpaceRect
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.layerStackSpaceRect = desiredLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialWidth = 1024;
+ constexpr uint32_t initialHeight = 768;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state has a size set
+ display.mutableCurrentDisplayState().width = initialWidth;
+ display.mutableCurrentDisplayState().height = initialHeight;
+
+ // The incoming request sets the same display size
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.width = initialWidth;
+ state.height = initialHeight;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
+ EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialWidth = 0;
+ constexpr uint32_t desiredWidth = 1024;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display does not yet have a width
+ display.mutableCurrentDisplayState().width = initialWidth;
+
+ // The incoming request sets a display width
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.width = desiredWidth;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialHeight = 0;
+ constexpr uint32_t desiredHeight = 768;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display does not yet have a height
+ display.mutableCurrentDisplayState().height = initialHeight;
+
+ // The incoming request sets a display height
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.height = desiredHeight;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
new file mode 100644
index 0000000..2117628
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -0,0 +1,498 @@
+/*
+ * 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"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+// Used when we simulate a display that supports doze.
+template <typename Display>
+struct DozeIsSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = true;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::DOZE;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::DOZE_SUSPEND;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(
+ std::vector<DisplayCapability>({DisplayCapability::DOZE})),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+// Used when we simulate a display that does not support doze.
+struct DozeNotSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = false;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::ON;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::ON;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
+};
+
+struct EventThreadBaseSupportedVariant {
+ static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
+ // The callback should not be notified to toggle VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
+
+ // The event thread should not be notified.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ }
+};
+
+struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupVsyncAndEventThreadNoCallExpectations(test);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupVsyncAndEventThreadNoCallExpectations(test);
+ }
+};
+
+struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The callback should be notified to enable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
+
+ // The event thread should be notified that the screen was acquired.
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The callback should be notified to disable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
+
+ // The event thread should not be notified that the screen was released.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ }
+};
+
+struct DispSyncIsSupportedVariant {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_REFRESH_RATE)).Times(1);
+ EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
+ }
+};
+
+struct DispSyncNotSupportedVariant {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// There are a large number of transitions we could test, however we only test a
+// selected subset which provides complete test coverage of the implementation.
+// --------------------------------------------------------------------
+
+template <PowerMode initialPowerMode, PowerMode targetPowerMode>
+struct TransitionVariantCommon {
+ static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
+ static constexpr auto TARGET_POWER_MODE = targetPowerMode;
+
+ static void verifyPostconditions(DisplayTransactionTest*) {}
+};
+
+struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOffToDozeSuspendVariant
+ : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionDozeSuspendToOffVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeSuspendToDozeVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionDozeSuspendToOnVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionOnToDozeSuspendVariant
+ : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ }
+};
+
+struct TransitionOnToUnknownVariant
+ : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupNoComposerPowerModeCallExpectations(test);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// Rather than testing the cartesian product of
+// DozeIsSupported/DozeNotSupported with all other options, we use one for one
+// display type, and the other for another display type.
+// --------------------------------------------------------------------
+
+template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
+ typename DispSyncVariant, typename TransitionVariant>
+struct DisplayPowerCase {
+ using Display = DisplayVariant;
+ using Doze = DozeVariant;
+ using EventThread = EventThreadVariant;
+ using DispSync = DispSyncVariant;
+ using Transition = TransitionVariant;
+
+ static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+ Display::injectHwcDisplayWithNoDefaultCapabilities(test);
+ auto display = Display::makeFakeExistingDisplayInjector(test);
+ display.inject();
+ display.mutableDisplayDevice()->setPowerMode(mode);
+ return display;
+ }
+
+ static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
+ test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
+ }
+
+ static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mMessageQueue, invalidate()).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, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
+ Return(Error::NONE)));
+
+ // Any calls to get whether the display supports dozing will return the value set by the
+ // policy variant.
+ EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
+ }
+
+ static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
+ }
+};
+
+// A sample configuration for the primary display.
+// In addition to having event thread support, we emulate doze support.
+template <typename TransitionVariant>
+using PrimaryDisplayPowerCase =
+ DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+ EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+ TransitionVariant>;
+
+// A sample configuration for the external display.
+// In addition to not having event thread support, we emulate not having doze
+// support.
+template <typename TransitionVariant>
+using ExternalDisplayPowerCase =
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+ EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+ TransitionVariant>;
+
+class SetPowerModeInternalTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void transitionDisplayCommon();
+};
+
+template <PowerMode PowerMode>
+struct PowerModeInitialVSyncEnabled : public std::false_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
+
+template <typename Case>
+void SetPowerModeInternalTest::transitionDisplayCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ Case::Doze::setupComposerCallExpectations(this);
+ auto display =
+ Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
+ Case::setInitialPrimaryHWVsyncEnabled(this,
+ PowerModeInitialVSyncEnabled<
+ Case::Transition::INITIAL_POWER_MODE>::value);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
+ Case::Transition::template setupCallExpectations<Case>(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
+ Case::Transition::TARGET_POWER_MODE);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ Case::Transition::verifyPostconditions(this);
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is already set to PowerMode::ON
+ display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+ mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+
+ // A virtual display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is set to PowerMode::ON
+ getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
new file mode 100644
index 0000000..61f0788
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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 hal::RenderIntent;
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, and the display should respond with an non-empty list of supported
+// color modes. Wide-color support should be configured.
+template <typename Display>
+struct WideColorP3ColorimetricSupportedVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = true;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
+ test->mFlinger.mutableHasWideColorDisplay() = 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>(
+ std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
+ RenderIntent::COLORIMETRIC))
+ .WillOnce(Return(Error::NONE));
+ }
+};
+
+template <typename Display>
+struct Hdr10PlusSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = true;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
+ Hdr::HDR10_PLUS,
+ Hdr::HDR10,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HDR10, so HDR10 support should be configured.
+template <typename Display>
+struct Hdr10SupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
+ Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HLG, so HLG support should be configured.
+template <typename Display>
+struct HdrHlgSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = true;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(
+ DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
+template <typename Display>
+struct HdrDolbyVisionSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Smpte2086PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ PerFrameMetadataKey::WHITE_POINT_X,
+ PerFrameMetadataKey::WHITE_POINT_Y,
+ PerFrameMetadataKey::MAX_LUMINANCE,
+ PerFrameMetadataKey::MIN_LUMINANCE,
+ })));
+ }
+};
+
+template <typename Display>
+struct Cta861_3_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+ PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ })));
+ }
+};
+
+template <typename Display>
+struct Hdr10_Plus_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::HDR10_PLUS_SEI,
+ })));
+ }
+};
+
+using WideColorP3ColorimetricDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10PlusDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrHlgDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrHlgSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrDolbyVisionDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrSmpte2086DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrCta861_3_DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+
+class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
+public:
+ template <typename T>
+ void setupNewDisplayDeviceInternalTest();
+};
+
+template <typename Case>
+void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
+ const sp<BBinder> displayToken = new BBinder();
+ const sp<compositionengine::mock::DisplaySurface> displaySurface =
+ new compositionengine::mock::DisplaySurface();
+ const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Wide color displays support is configured appropriately
+ Case::WideColorSupport::injectConfigChange(this);
+
+ // The display is setup with the HWC.
+ Case::Display::injectHwcDisplay(this);
+
+ // SurfaceFlinger will use a test-controlled factory for native window
+ // surfaces.
+ injectFakeNativeWindowSurfaceFactory();
+
+ // A compositionengine::Display has already been created
+ auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // Various native window calls will be made.
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ DisplayDeviceState state;
+ if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
+ }
+
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ displaySurface, producer);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ ASSERT_TRUE(device != nullptr);
+ EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+ EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
+ EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+ EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
+ EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
+ EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
+ EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
+ EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
+ EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
+ EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
+ // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
+ // remapped, and the test only ever sets up one config. If there were an error
+ // looking up the remapped index, device->getActiveConfig() would be -1 instead.
+ EXPECT_EQ(0, device->getActiveConfig().value());
+ EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
+ device->getSupportedPerFrameMetadata());
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
+ setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
+ setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
+ setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
+ setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
+ setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
+ setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
+ setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
+ setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
+ setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ebb2d76..a9d9dc0 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -16,15 +16,17 @@
#pragma once
+#include <Scheduler/Scheduler.h>
#include <gmock/gmock.h>
#include <gui/ISurfaceComposer.h>
-#include "Scheduler/DispSync.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VSyncTracker.h"
-#include "mock/MockDispSync.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -32,14 +34,16 @@
public:
TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
bool useContentDetectionV2)
- : TestableScheduler(std::make_unique<mock::DispSync>(), configs, callback,
+ : TestableScheduler(std::make_unique<mock::VsyncController>(),
+ std::make_unique<mock::VSyncTracker>(), configs, callback,
useContentDetectionV2) {}
- TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
+ TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+ std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
bool useContentDetectionV2)
- : Scheduler({std::move(primaryDispSync), nullptr, nullptr}, configs, callback,
- createLayerHistory(configs, useContentDetectionV2),
+ : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
+ callback, createLayerHistory(configs, useContentDetectionV2),
{.supportKernelTimer = false,
.useContentDetection = true,
.useContentDetectionV2 = useContentDetectionV2}) {}
@@ -90,12 +94,28 @@
return mFeatures.touch == Scheduler::TouchState::Active;
}
+ void dispatchCachedReportedConfig() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ return Scheduler::dispatchCachedReportedConfig();
+ }
+
+ void clearOptionalFieldsInFeatures() {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mFeatures.cachedConfigChangedParams.reset();
+ }
+
+ void onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+ return Scheduler::onNonPrimaryDisplayConfigChanged(handle, displayId, configId,
+ vsyncPeriod);
+ }
+
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the Scheduler instance may
// still be referenced by something despite our best efforts to destroy
// it after each test is done.
- mVsyncSchedule.sync.reset();
+ mVsyncSchedule.controller.reset();
mConnections.clear();
}
};
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8c4232d..6ce738a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -40,6 +40,7 @@
#include "SurfaceInterceptor.h"
#include "TestableScheduler.h"
#include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/MockDisplayIdGenerator.h"
namespace android {
@@ -83,8 +84,8 @@
return nullptr;
}
- std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override {
- return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+ sp<SurfaceInterceptor> createSurfaceInterceptor() override {
+ return new android::impl::SurfaceInterceptor();
}
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
@@ -171,6 +172,9 @@
SurfaceFlinger* flinger() { return mFlinger.get(); }
TestableScheduler* scheduler() { return mScheduler; }
+ mock::DisplayIdGenerator<GpuVirtualDisplayId>& gpuVirtualDisplayIdGenerator() {
+ return mGpuVirtualDisplayIdGenerator;
+ }
// Extend this as needed for accessing SurfaceFlinger private (and public)
// functions.
@@ -189,7 +193,8 @@
}
// The ISchedulerCallback argument can be nullptr for a no-op implementation.
- void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
+ void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+ std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
@@ -217,9 +222,9 @@
mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
constexpr bool kUseContentDetectionV2 = false;
- mScheduler =
- new TestableScheduler(std::move(primaryDispSync), *mFlinger->mRefreshRateConfigs,
- *(callback ?: this), kUseContentDetectionV2);
+ mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ *mFlinger->mRefreshRateConfigs, *(callback ?: this),
+ kUseContentDetectionV2);
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -272,6 +277,10 @@
layer->mPotentialCursor = potentialCursor;
}
+ static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
+ layer->mDrawingParent = drawingParent;
+ }
+
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -284,8 +293,6 @@
return mFlinger->destroyDisplay(displayToken);
}
- auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); }
-
auto setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -324,7 +331,9 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
+ auto onMessageReceived(int32_t what) {
+ return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime());
+ }
auto renderScreenImplLocked(const RenderArea& renderArea,
SurfaceFlinger::TraverseLayersFunction traverseLayers,
@@ -347,20 +356,26 @@
auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
- auto setTransactionState(const Vector<ComposerState>& states,
+ auto setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
bool hasListenerCallbacks,
- std::vector<ListenerCallbacks>& listenerCallbacks) {
- return mFlinger->setTransactionState(states, displays, flags, applyToken,
- inputWindowCommands, desiredPresentTime, uncacheBuffer,
- hasListenerCallbacks, listenerCallbacks);
+ std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) {
+ return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags,
+ applyToken, inputWindowCommands, desiredPresentTime,
+ uncacheBuffer, hasListenerCallbacks, listenerCallbacks,
+ transactionId);
}
auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+ auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ return mFlinger->onTransact(code, data, reply, flags);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -419,7 +434,7 @@
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
mutableEventQueue().reset();
- mutableInterceptor().reset();
+ mutableInterceptor().clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
mFlinger->mCompositionEngine->setRenderEngine(
@@ -456,7 +471,8 @@
static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON;
- FakeHwcDisplayInjector(DisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary)
+ FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+ bool isPrimary)
: mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
auto& setHwcDisplayId(hal::HWDisplayId displayId) {
@@ -529,14 +545,16 @@
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
- flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
+ const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
+ LOG_ALWAYS_FATAL_IF(!physicalId);
+ flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
(mIsPrimary ? flinger->mutableInternalHwcDisplayId()
: flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
}
}
private:
- const DisplayId mDisplayId;
+ const HalDisplayId mDisplayId;
const hal::DisplayType mHwcDisplayType;
const bool mIsPrimary;
@@ -628,10 +646,10 @@
DisplayDeviceState state;
if (const auto type = mCreationArgs.connectionType) {
LOG_ALWAYS_FATAL_IF(!displayId);
+ const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
+ LOG_ALWAYS_FATAL_IF(!physicalId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
- .type = *type,
- .hwcDisplayId = *mHwcDisplayId};
+ state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
}
state.isSecure = mCreationArgs.isSecure;
@@ -665,6 +683,7 @@
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
Hwc2::mock::Display mDisplay;
+ mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimerTest.cpp b/services/surfaceflinger/tests/unittests/TimerTest.cpp
new file mode 100644
index 0000000..cda6bbf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimerTest.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "AsyncCallRecorder.h"
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/Timer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+struct TimerTest : testing::Test {
+ static constexpr int mIterations = 20;
+
+ AsyncCallRecorder<void (*)()> mCallbackRecorder;
+ Timer mTimer;
+
+ void timerCallback() { mCallbackRecorder.recordCall(); }
+};
+
+TEST_F(TimerTest, callsCallbackIfScheduledInPast) {
+ for (int i = 0; i < mIterations; i++) {
+ mTimer.alarmAt(std::bind(&TimerTest::timerCallback, this), systemTime() - 10'000'00);
+ EXPECT_TRUE(mCallbackRecorder.waitForCall().has_value());
+ EXPECT_FALSE(mCallbackRecorder.waitForUnexpectedCall().has_value());
+ }
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 44b3dc0..760bf65 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -31,9 +31,9 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
-#include "mock/MockDispSync.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
namespace android {
@@ -75,11 +75,12 @@
new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
- EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
- EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mVSyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
- mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
+ mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
+ std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
std::move(eventThread), std::move(sfEventThread));
}
@@ -89,7 +90,8 @@
std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
- mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+ mock::VsyncController* mVsyncController = new mock::VsyncController();
+ mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
struct TransactionInfo {
Vector<ComposerState> states;
@@ -98,7 +100,9 @@
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
InputWindowCommands inputWindowCommands;
int64_t desiredPresentTime = -1;
+ int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
client_cache_t uncacheBuffer;
+ int64_t id = -1;
};
void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
@@ -112,26 +116,28 @@
}
void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
- int64_t desiredPresentTime) {
+ int64_t desiredPresentTime, int64_t frameTimelineVsyncId) {
mTransactionNumber++;
transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
transaction.desiredPresentTime = desiredPresentTime;
+ transaction.frameTimelineVsyncId = frameTimelineVsyncId;
}
void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillOnce(Return(systemTime()));
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ -1);
+ /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
nsecs_t applicationTime = systemTime();
- mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+ mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+ transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ mHasListenerCallbacks, mCallbacks, transaction.id);
// This transaction should not have been placed on the transaction queue.
// If transaction is synchronous or syncs input windows, SF
@@ -156,16 +162,17 @@
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
nsecs_t time = systemTime();
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
.WillOnce(Return(time + nsecs_t(5 * 1e8)));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ time + s2ns(1));
+ /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+ mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
+ transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ mHasListenerCallbacks, mCallbacks, transaction.id);
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
@@ -179,24 +186,25 @@
// called in SurfaceFlinger::signalTransaction
nsecs_t time = systemTime();
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
.WillOnce(Return(time + nsecs_t(5 * 1e8)));
// transaction that should go on the pending thread
TransactionInfo transactionA;
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ time + s2ns(1));
+ /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
// transaction that would not have gone on the pending thread if not
// blocked
TransactionInfo transactionB;
setupSingle(transactionB, flags, syncInputWindows,
- /*desiredPresentTime*/ -1);
+ /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+ mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
+ transactionA.displays, transactionA.flags,
transactionA.applyToken, transactionA.inputWindowCommands,
transactionA.desiredPresentTime, transactionA.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ mHasListenerCallbacks, mCallbacks, transactionA.id);
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -204,10 +212,11 @@
EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags,
+ mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states,
+ transactionB.displays, transactionB.flags,
transactionB.applyToken, transactionB.inputWindowCommands,
transactionB.desiredPresentTime, transactionB.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ mHasListenerCallbacks, mCallbacks, transactionB.id);
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -244,16 +253,17 @@
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
// nsecs_t time = systemTime();
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
.WillOnce(Return(nsecs_t(5 * 1e8)))
.WillOnce(Return(s2ns(2)));
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ s2ns(1));
- mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
- transactionA.applyToken, transactionA.inputWindowCommands,
- transactionA.desiredPresentTime, transactionA.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks);
+ /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+ mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
+ transactionA.displays, transactionA.flags, transactionA.applyToken,
+ transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_EQ(1, transactionQueue.size());
@@ -269,9 +279,10 @@
// different process) to re-query and reset the cached expected present time
TransactionInfo empty;
empty.applyToken = sp<IBinder>();
- mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken,
- empty.inputWindowCommands, empty.desiredPresentTime,
- empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks);
+ mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays,
+ empty.flags, empty.applyToken, empty.inputWindowCommands,
+ empty.desiredPresentTime, empty.uncacheBuffer,
+ mHasListenerCallbacks, mCallbacks, empty.id);
// flush transaction queue should flush as desiredPresentTime has
// passed
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c2a7752..1e5139c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -65,7 +65,7 @@
bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
auto const normalized_to_base = time_point - mBase;
auto const floor = (normalized_to_base) % mPeriod;
if (floor == 0) {
@@ -75,13 +75,13 @@
}
void set_interval(nsecs_t interval, nsecs_t last_known) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
mPeriod = interval;
mBase = last_known;
}
nsecs_t currentPeriod() const final {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
return mPeriod;
}
@@ -104,30 +104,36 @@
class RepeatingCallbackReceiver {
public:
- RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
- : mWorkload(wl),
+ RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration)
+ : mWorkload(workload),
+ mReadyDuration(readyDuration),
mCallback(
- dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
+ dispatch, [&](auto time, auto, auto) { callback_called(time); }, "repeat0") {}
void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
mCallbackTimes.reserve(iterations);
- mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
+ mCallback.schedule(
+ {.workDuration = mWorkload,
+ .readyDuration = mReadyDuration,
+ .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
for (auto i = 0u; i < iterations - 1; i++) {
- std::unique_lock<decltype(mMutex)> lk(mMutex);
- mCv.wait(lk, [&] { return mCalled; });
+ std::unique_lock lock(mMutex);
+ mCv.wait(lock, [&] { return mCalled; });
mCalled = false;
auto last = mLastTarget;
- lk.unlock();
+ lock.unlock();
onEachFrame(last);
- mCallback.schedule(mWorkload, last + mWorkload);
+ mCallback.schedule({.workDuration = mWorkload,
+ .readyDuration = mReadyDuration,
+ .earliestVsync = last + mWorkload + mReadyDuration});
}
// wait for the last callback.
- std::unique_lock<decltype(mMutex)> lk(mMutex);
- mCv.wait(lk, [&] { return mCalled; });
+ std::unique_lock lock(mMutex);
+ mCv.wait(lock, [&] { return mCalled; });
}
void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
@@ -136,7 +142,7 @@
private:
void callback_called(nsecs_t time) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
mCallbackTimes.push_back(time);
mCalled = true;
mLastTarget = time;
@@ -144,6 +150,7 @@
}
nsecs_t const mWorkload;
+ nsecs_t const mReadyDuration;
VSyncCallbackRegistration mCallback;
std::mutex mMutex;
@@ -160,9 +167,9 @@
static size_t constexpr num_clients = 3;
std::array<RepeatingCallbackReceiver, num_clients>
- cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
- RepeatingCallbackReceiver(dispatch, toNs(0h)),
- RepeatingCallbackReceiver(dispatch, toNs(1ms))};
+ cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us), toNs(2500us)),
+ RepeatingCallbackReceiver(dispatch, toNs(0h), toNs(0h)),
+ RepeatingCallbackReceiver(dispatch, toNs(1ms), toNs(3ms))};
auto const on_each_frame = [](nsecs_t) {};
std::array<std::thread, num_clients> threads{
@@ -187,7 +194,7 @@
VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
mVsyncMoveThreshold);
- RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+ RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
auto const on_each_frame = [&](nsecs_t last_known) {
tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
@@ -205,7 +212,7 @@
VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
mVsyncMoveThreshold);
- RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+ RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
auto jump_frame_counter = 0u;
auto constexpr jump_frame_at = 10u;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 373f6a5..69731fd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -64,19 +64,19 @@
class ControllableClock : public TimeKeeper {
public:
ControllableClock() {
- ON_CALL(*this, alarmIn(_, _))
- .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+ ON_CALL(*this, alarmAt(_, _))
+ .WillByDefault(Invoke(this, &ControllableClock::alarmAtDefaultBehavior));
ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
}
MOCK_CONST_METHOD0(now, nsecs_t());
- MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+ MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time));
MOCK_METHOD0(alarmCancel, void());
MOCK_CONST_METHOD1(dump, void(std::string&));
- void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+ void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
mCallback = callback;
- mNextCallbackTime = time + mCurrentTime;
+ mNextCallbackTime = time;
}
nsecs_t fakeTime() const { return mCurrentTime; }
@@ -109,22 +109,24 @@
CountingCallback(VSyncDispatch& dispatch)
: mDispatch(dispatch),
mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
- std::placeholders::_1,
- std::placeholders::_2),
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3),
"test")) {}
~CountingCallback() { mDispatch.unregisterCallback(mToken); }
operator VSyncDispatch::CallbackToken() const { return mToken; }
- void counter(nsecs_t time, nsecs_t wakeup_time) {
+ void counter(nsecs_t time, nsecs_t wakeup_time, nsecs_t readyTime) {
mCalls.push_back(time);
mWakeupTime.push_back(wakeup_time);
+ mReadyTime.push_back(readyTime);
}
VSyncDispatch& mDispatch;
VSyncDispatch::CallbackToken mToken;
std::vector<nsecs_t> mCalls;
std::vector<nsecs_t> mWakeupTime;
+ std::vector<nsecs_t> mReadyTime;
};
class PausingCallback {
@@ -142,18 +144,18 @@
operator VSyncDispatch::CallbackToken() const { return mToken; }
void pause(nsecs_t, nsecs_t) {
- std::unique_lock<std::mutex> lk(mMutex);
+ std::unique_lock lock(mMutex);
mPause = true;
mCv.notify_all();
- mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+ mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; });
mResourcePresent = (mResource.lock() != nullptr);
}
bool waitForPause() {
- std::unique_lock<std::mutex> lk(mMutex);
- auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+ std::unique_lock lock(mMutex);
+ auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; });
return waiting;
}
@@ -162,7 +164,7 @@
bool resourcePresent() { return mResourcePresent; }
void unpause() {
- std::unique_lock<std::mutex> lk(mMutex);
+ std::unique_lock lock(mMutex);
mPause = false;
mCv.notify_all();
}
@@ -192,8 +194,8 @@
class TimeKeeperWrapper : public TimeKeeper {
public:
TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
- void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
- mControllableClock.alarmIn(callback, time);
+ void alarmAt(std::function<void()> const& callback, nsecs_t time) final {
+ mControllableClock.alarmAt(callback, time);
}
void alarmCancel() final { mControllableClock.alarmCancel(); }
nsecs_t now() const final { return mControllableClock.now(); }
@@ -222,22 +224,30 @@
};
TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
{
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
mVsyncMoveThreshold};
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
}
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
auto intended = mPeriod - 230;
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = intended}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -246,10 +256,10 @@
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
- EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+ EXPECT_CALL(mMockClock, alarmAt(_, 1050));
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, 100, mPeriod);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -262,37 +272,53 @@
auto const workDuration = 10 * mPeriod;
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
.WillOnce(Return(mPeriod * 11));
- EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+ EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = workDuration,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(950);
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
}
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -302,14 +328,18 @@
}
TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
auto resource = std::make_shared<int>(110);
PausingCallback cb(mDispatch, 50ms);
cb.stashResource(resource);
- EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = mPeriod}),
+ ScheduleResult::Scheduled);
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
EXPECT_TRUE(cb.waitForPause());
@@ -332,15 +362,15 @@
.WillOnce(Return(1075));
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 955)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 813)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 975)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 100, mPeriod);
- mDispatch.schedule(cb1, 250, mPeriod);
+ mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+ mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
advanceToNextCallback();
advanceToNextCallback();
@@ -360,53 +390,54 @@
.WillOnce(Return(10000));
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 750)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 100, mPeriod * 10);
- mDispatch.schedule(cb1, 250, mPeriod);
+ mDispatch.schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
+ mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
mDispatch.cancel(cb1);
}
TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 400, 1000);
- mDispatch.schedule(cb1, 200, 1000);
- mDispatch.schedule(cb1, 300, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 400, 1000);
- mDispatch.schedule(cb1, 200, 1000);
- mDispatch.schedule(cb1, 500, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1590)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
auto offset = 400;
auto closeOffset = offset + mDispatchGroupThreshold - 1;
@@ -415,9 +446,10 @@
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 400, 1000);
- mDispatch.schedule(cb1, 200, 1000);
- mDispatch.schedule(cb1, closeOffset, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1,
+ {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -425,8 +457,9 @@
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
- mDispatch.schedule(cb0, 400, 2000);
- mDispatch.schedule(cb1, notCloseOffset, 2000);
+ mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch.schedule(cb1,
+ {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000});
advanceToNextCallback();
ASSERT_THAT(cb1.mCalls.size(), Eq(2));
EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -437,16 +470,17 @@
}
TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
- EXPECT_CALL(mMockClock, alarmIn(_, 900));
- EXPECT_CALL(mMockClock, alarmIn(_, 800));
- EXPECT_CALL(mMockClock, alarmIn(_, 100));
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 800)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel());
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 100, 1000);
- mDispatch.schedule(cb1, 200, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
}
@@ -459,32 +493,38 @@
.WillOnce(Return(2950));
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, 100, 920);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
mMockClock.advanceBy(850);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
- mDispatch.schedule(cb, 100, 1900);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
mMockClock.advanceBy(900);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
mMockClock.advanceBy(125);
EXPECT_THAT(cb.mCalls.size(), Eq(2));
- mDispatch.schedule(cb, 100, 2900);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
mMockClock.advanceBy(975);
EXPECT_THAT(cb.mCalls.size(), Eq(3));
}
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
VSyncDispatch::CallbackToken tmp;
- tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
- "o.o");
+ tmp = mDispatch.registerCallback(
+ [&](auto, auto, auto) {
+ mDispatch.schedule(tmp,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 2000});
+ },
+ "o.o");
- mDispatch.schedule(tmp, 100, 1000);
+ mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
}
@@ -492,17 +532,27 @@
VSyncDispatch::CallbackToken tmp;
std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
- [&](auto timestamp, auto) {
- EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+ [&](auto timestamp, auto, auto) {
+ EXPECT_EQ(mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp - mVsyncMoveThreshold}),
ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+ EXPECT_EQ(mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(tmp,
+ {.workDuration = 400,
+ .readyDuration = 0,
+ .earliestVsync = timestamp + mVsyncMoveThreshold}),
ScheduleResult::Scheduled);
lastTarget = timestamp;
},
"oo");
- mDispatch.schedule(tmp, 999, 1000);
+ mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
EXPECT_THAT(lastTarget, Eq(1000));
@@ -512,40 +562,40 @@
TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1000)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 950)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1950)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb(mDispatch);
- mDispatch.schedule(cb, 0, 1000);
+ mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
mMockClock.advanceBy(750);
- mDispatch.schedule(cb, 50, 1000);
+ mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- mDispatch.schedule(cb, 50, 2000);
+ mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
mMockClock.advanceBy(800);
- mDispatch.schedule(cb, 100, 2000);
+ mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
}
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 850)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1800)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 500, 1000);
- mDispatch.schedule(cb1, 100, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
- mDispatch.schedule(cb0, 200, 2000);
- mDispatch.schedule(cb1, 150, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
+ mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
advanceToNextCallback();
advanceToNextCallback();
@@ -553,46 +603,58 @@
TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
CountingCallback cb0(mDispatch);
CountingCallback cb1(mDispatch);
- mDispatch.schedule(cb0, 500, 1000);
- mDispatch.schedule(cb1, 500, 20000);
+ mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
}
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
CountingCallback cb0(mDispatch);
- mDispatch.schedule(cb0, 500, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
mDispatch.cancel(cb0);
- mDispatch.schedule(cb0, 100, 1000);
+ mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
}
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
VSyncDispatch::CallbackToken token(100);
- EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+ EXPECT_THAT(mDispatch.schedule(token,
+ {.workDuration = 100,
+ .readyDuration = 0,
+ .earliestVsync = 1000}),
+ Eq(ScheduleResult::Error));
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
}
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
}
// b/1450138150
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
- EXPECT_CALL(mMockClock, alarmIn(_, 500));
+ EXPECT_CALL(mMockClock, alarmAt(_, 500));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
ASSERT_THAT(cb.mCalls.size(), Eq(1));
}
@@ -603,83 +665,103 @@
.WillOnce(Return(1000))
.WillOnce(Return(1002));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(400);
- EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
CountingCallback cb0(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0,
+ {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
- EXPECT_CALL(mMockClock, alarmIn(_, 600));
+ EXPECT_CALL(mMockClock, alarmAt(_, 600));
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
advanceToNextCallback();
}
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
- mDispatch, [](auto, auto) {}, "");
+ mDispatch, [](auto, auto, auto) {}, "");
VSyncCallbackRegistration cb1(std::move(cb));
- cb.schedule(100, 1000);
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
cb.cancel();
- cb1.schedule(500, 1000);
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
cb1.cancel();
}
TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
- EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+ EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
VSyncCallbackRegistration cb(
- mDispatch, [](auto, auto) {}, "");
+ mDispatch, [](auto, auto, auto) {}, "");
VSyncCallbackRegistration cb1(
- mDispatch, [](auto, auto) {}, "");
+ mDispatch, [](auto, auto, auto) {}, "");
cb1 = std::move(cb);
- cb.schedule(100, 1000);
+ cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
cb.cancel();
- cb1.schedule(500, 1000);
+ cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
cb1.cancel();
}
// b/154303580
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(80);
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -691,16 +773,20 @@
// update later, as opposed to blocking the calling thread.
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
CountingCallback cb(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
- EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.advanceBy(80);
EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -709,13 +795,17 @@
// b/154303580.
TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -730,14 +820,18 @@
TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
Sequence seq;
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
- EXPECT_CALL(mMockClock, alarmIn(_, 1280)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
CountingCallback cb1(mDispatch);
CountingCallback cb2(mDispatch);
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(620);
@@ -760,21 +854,49 @@
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
.InSequence(seq)
.WillOnce(Return(1000));
- EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb2, 390, 1000), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb1,
+ {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(cb2,
+ {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
+ ScheduleResult::Scheduled);
mMockClock.setLag(100);
mMockClock.advanceBy(700);
ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1));
EXPECT_THAT(cb1.mWakeupTime[0], Eq(600));
+ ASSERT_THAT(cb1.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb1.mReadyTime[0], Eq(1000));
ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1));
EXPECT_THAT(cb2.mWakeupTime[0], Eq(610));
+ ASSERT_THAT(cb2.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb2.mReadyTime[0], Eq(1000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) {
+ auto intended = mPeriod - 230;
+ EXPECT_CALL(mMockClock, alarmAt(_, 900));
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb,
+ {.workDuration = 70,
+ .readyDuration = 30,
+ .earliestVsync = intended}),
+ ScheduleResult::Scheduled);
+ advanceToNextCallback();
+
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+ EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+ ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
+ EXPECT_THAT(cb.mWakeupTime[0], 900);
+ ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
+ EXPECT_THAT(cb.mReadyTime[0], 970);
}
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
@@ -787,7 +909,7 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
std::string name("basicname");
VSyncDispatchTimerQueueEntry entry(
- name, [](auto, auto) {}, mVsyncMoveThreshold);
+ name, [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.name(), Eq(name));
EXPECT_FALSE(entry.lastExecutedVsyncTarget());
EXPECT_FALSE(entry.wakeupTime());
@@ -795,10 +917,12 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
@@ -815,10 +939,12 @@
.Times(1)
.WillOnce(Return(10000));
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+ mStubTracker, now),
+ Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(9500));
@@ -828,21 +954,29 @@
auto callCount = 0;
auto vsyncCalledTime = 0;
auto wakeupCalledTime = 0;
+ auto readyCalledTime = 0;
VSyncDispatchTimerQueueEntry entry(
"test",
- [&](auto vsyncTime, auto wakeupTime) {
+ [&](auto vsyncTime, auto wakeupTime, auto readyTime) {
callCount++;
vsyncCalledTime = vsyncTime;
wakeupCalledTime = wakeupTime;
+ readyCalledTime = readyTime;
},
mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(*wakeup, Eq(900));
- entry.callback(entry.executing(), *wakeup);
+ auto const ready = entry.readyTime();
+ ASSERT_TRUE(ready);
+ EXPECT_THAT(*ready, Eq(1000));
+
+ entry.callback(entry.executing(), *wakeup, *ready);
EXPECT_THAT(callCount, Eq(1));
EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
@@ -860,13 +994,15 @@
.WillOnce(Return(1020));
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
auto wakeup = entry.wakeupTime();
ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
@@ -879,8 +1015,10 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
auto const wakeup = entry.wakeupTime();
@@ -890,24 +1028,35 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
entry.executing(); // 1000 is executing
// had 1000 not been executing, this could have been scheduled for time 800.
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+ EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+ EXPECT_THAT(*entry.readyTime(), Eq(2000));
- EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+ EXPECT_THAT(*entry.readyTime(), Eq(2000));
}
TEST_F(VSyncDispatchTimerQueueEntryTest,
willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
Sequence seq;
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
@@ -920,35 +1069,85 @@
.InSequence(seq)
.WillOnce(Return(2000));
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
entry.executing(); // 1000 is executing
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
- EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
static constexpr auto effectualOffset = 200;
VSyncDispatchTimerQueueEntry entry(
- "test", [](auto, auto) {}, mVsyncMoveThreshold);
+ "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
- entry.addPendingWorkloadUpdate(100, 400);
- entry.addPendingWorkloadUpdate(effectualOffset, 700);
+ entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400});
+ entry.addPendingWorkloadUpdate(
+ {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
}
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) {
+ auto callCount = 0;
+ auto vsyncCalledTime = 0;
+ auto wakeupCalledTime = 0;
+ auto readyCalledTime = 0;
+ VSyncDispatchTimerQueueEntry entry(
+ "test",
+ [&](auto vsyncTime, auto wakeupTime, auto readyTime) {
+ callCount++;
+ vsyncCalledTime = vsyncTime;
+ wakeupCalledTime = wakeupTime;
+ readyCalledTime = readyTime;
+ },
+ mVsyncMoveThreshold);
+
+ EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+ mStubTracker, 0),
+ Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(900));
+
+ auto const ready = entry.readyTime();
+ ASSERT_TRUE(ready);
+ EXPECT_THAT(*ready, Eq(970));
+
+ entry.callback(entry.executing(), *wakeup, *ready);
+
+ EXPECT_THAT(callCount, Eq(1));
+ EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
+ EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
+ EXPECT_FALSE(entry.wakeupTime());
+ auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+ ASSERT_TRUE(lastCalledTarget);
+ EXPECT_THAT(*lastCalledTarget, Eq(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 c5cddf3..0dcaf26 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -63,64 +63,46 @@
class MockVSyncDispatch : public VSyncDispatch {
public:
MOCK_METHOD2(registerCallback,
- CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
- MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+ MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-std::shared_ptr<FenceTime> generateInvalidFence() {
+std::shared_ptr<android::FenceTime> generateInvalidFence() {
sp<Fence> fence = new Fence();
- return std::make_shared<FenceTime>(fence);
+ return std::make_shared<android::FenceTime>(fence);
}
-std::shared_ptr<FenceTime> generatePendingFence() {
+std::shared_ptr<android::FenceTime> generatePendingFence() {
sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
- return std::make_shared<FenceTime>(fence);
+ return std::make_shared<android::FenceTime>(fence);
}
-void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
- FenceTime::Snapshot snap(time);
+void signalFenceWithTime(std::shared_ptr<android::FenceTime> const& fence, nsecs_t time) {
+ android::FenceTime::Snapshot snap(time);
fence->applyTrustedSnapshot(snap);
}
-std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
- std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+ std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence);
signalFenceWithTime(ft, time);
return ft;
}
-class StubCallback : public DispSync::Callback {
-public:
- void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
- std::lock_guard<std::mutex> lk(mMutex);
- mLastCallTime = when;
- }
- std::optional<nsecs_t> lastCallTime() const {
- std::lock_guard<std::mutex> lk(mMutex);
- return mLastCallTime;
- }
-
-private:
- std::mutex mutable mMutex;
- std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
-};
-
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
- : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
- mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
+ : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
- mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockDispatch, *mMockTracker,
- kPendingLimit, false /* supportKernelIdleTimer */) {
+ mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit,
+ false /* supportKernelIdleTimer */) {
ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
}
- std::shared_ptr<MockVSyncDispatch> mMockDispatch;
std::shared_ptr<MockVSyncTracker> mMockTracker;
std::shared_ptr<MockClock> mMockClock;
static constexpr size_t kPendingLimit = 3;
@@ -135,7 +117,7 @@
VSyncDispatch::CallbackToken const mFakeToken{2398};
nsecs_t lastCallbackTime = 0;
- StubCallback outerCb;
+ // StubCallback outerCb;
std::function<void(nsecs_t, nsecs_t)> innerCb;
VSyncReactor mReactor;
@@ -170,7 +152,7 @@
}
TEST_F(VSyncReactorTest, limitsPendingFences) {
- std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+ std::array<std::shared_ptr<android::FenceTime>, kPendingLimit * 2> fences;
std::array<nsecs_t, fences.size()> fakeTimes;
std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
@@ -211,86 +193,48 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
- nsecs_t const fakeTimestamp = 4839;
- EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
- .Times(1)
- .WillOnce(Return(fakeTimestamp));
-
- EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
- nsecs_t const fakeTimestamp = 4839;
- EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
- .Times(1)
- .WillOnce(Return(fakeTimestamp));
-
- EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
- nsecs_t const fakeTimestamp = 4839;
- nsecs_t const fakePeriod = 1010;
- nsecs_t const mFakeNow = 2214;
- int const numPeriodsOut = 3;
- EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
- EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
- .WillOnce(Return(fakeTimestamp));
- EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, getPeriod) {
- nsecs_t const fakePeriod = 1010;
- EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
- EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
-}
-
TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(20000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
- EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
nsecs_t sampleTime = 0;
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.setPeriod(period);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ mReactor.startPeriodTransition(period);
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -299,16 +243,18 @@
nsecs_t const secondPeriod = 5000;
nsecs_t const thirdPeriod = 2000;
- mReactor.setPeriod(secondPeriod);
+ mReactor.startPeriodTransition(secondPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.setPeriod(thirdPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
+ mReactor.startPeriodTransition(thirdPeriod);
+ EXPECT_TRUE(
+ mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(
+ mReactor.addHwVsyncTimestamp(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -323,9 +269,10 @@
nsecs_t skewyPeriod = period >> 1;
bool periodFlushed = false;
nsecs_t sampleTime = 0;
- EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(
+ mReactor.addHwVsyncTimestamp(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -343,22 +290,22 @@
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
nsecs_t const newPeriod = 5000;
EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
bool periodFlushed = true;
- EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
Mock::VerifyAndClearExpectations(mMockTracker.get());
EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
- EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
}
@@ -367,7 +314,7 @@
bool periodFlushed = false;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
- EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(fakeTimestamp, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -375,23 +322,23 @@
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
auto time = 0;
auto constexpr numTimestampSubmissions = 10;
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += period;
- EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
time += newPeriod;
- EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
for (auto i = 0; i < numTimestampSubmissions; i++) {
time += newPeriod;
- EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
}
@@ -400,14 +347,14 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
time += period;
- mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+ mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
time += newPeriod;
- mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+ mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
@@ -416,7 +363,7 @@
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
static auto constexpr numSamplesWithNewPeriod = 4;
Sequence seq;
@@ -430,20 +377,20 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
// confirmed period, but predictor wants numRequest samples. This one and prior are valid.
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
}
TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) {
auto time = 0;
bool periodFlushed = false;
nsecs_t const newPeriod = 4000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -452,9 +399,9 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
}
TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) {
@@ -463,7 +410,7 @@
nsecs_t const newPeriod1 = 4000;
nsecs_t const newPeriod2 = 7000;
- mReactor.setPeriod(newPeriod1);
+ mReactor.startPeriodTransition(newPeriod1);
Sequence seq;
EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -476,208 +423,17 @@
.WillRepeatedly(Return(false));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7);
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
// confirmed period, but predictor wants numRequest samples. This one and prior are valid.
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
- mReactor.setPeriod(newPeriod2);
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
- EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
-}
-
-static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
- return period - phase;
-}
-
-TEST_F(VSyncReactorTest, addEventListener) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.removeEventListener(&outerCb, &lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
- .Times(2)
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- ASSERT_TRUE(innerCb);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
-}
-
-TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
- .InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- ASSERT_TRUE(innerCb);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
- EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
-}
-
-TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-}
-
-// b/149221293
-TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
- class SelfRemovingCallback : public DispSync::Callback {
- public:
- SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
- void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
- mVsr.removeEventListener(this, &when);
- }
-
- private:
- VSyncReactor& mVsr;
- } selfRemover(mReactor);
-
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
- innerCb(0, 0);
-}
-
-TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
- EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
- static constexpr nsecs_t anotherPeriod = 23333;
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
- .InSequence(seq);
- EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
- .InSequence(seq);
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-
- bool periodFlushed = false;
- mReactor.setPeriod(anotherPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
- EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
- .InSequence(seq)
- .WillOnce(Return(ScheduleResult::Scheduled));
-
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
- .InSequence(seq)
- .WillOnce(Return(ScheduleResult::Scheduled));
-
- EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
- .InSequence(seq)
- .WillOnce(Return(ScheduleResult::Scheduled));
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
- ASSERT_TRUE(innerCb);
- innerCb(mFakeVSyncTime, mFakeWakeupTime);
-}
-
-TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
- nsecs_t const negativePhase = -4000;
- Sequence seq;
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .InSequence(seq)
- .WillOnce(Return(mFakeToken));
- EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
- .InSequence(seq);
- mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
-}
-
-TEST_F(VSyncReactorTest, beginResyncResetsModel) {
- EXPECT_CALL(*mMockTracker, resetModel());
- mReactor.beginResync();
+ mReactor.startPeriodTransition(newPeriod2);
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
}
TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
@@ -686,13 +442,13 @@
mReactor.setIgnorePresentFences(true);
nsecs_t const newPeriod = 5000;
- mReactor.setPeriod(newPeriod);
+ mReactor.startPeriodTransition(newPeriod);
- EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(newPeriod, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
@@ -700,9 +456,8 @@
TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
// Create a reactor which supports the kernel idle timer
- auto idleReactor =
- VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockDispatch, *mMockTracker,
- kPendingLimit, true /* supportKernelIdleTimer */);
+ auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
+ kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
@@ -710,66 +465,28 @@
// First, set the same period, which should only be confirmed when we receive two
// matching callbacks
- idleReactor.setPeriod(10000);
- EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+ idleReactor.startPeriodTransition(10000);
+ EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period but incorrect timestamp delta
- EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
+ EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Correct period and correct timestamp delta
- EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
+ EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(10000, 10000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
// Then, set a new period, which should be confirmed as soon as we receive a callback
// reporting the new period
nsecs_t const newPeriod = 5000;
- idleReactor.setPeriod(newPeriod);
+ idleReactor.startPeriodTransition(newPeriod);
// Incorrect timestamp delta and period
- EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
+ EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
EXPECT_FALSE(periodFlushed);
// Incorrect timestamp delta but correct period
- EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
+ EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
-using VSyncReactorDeathTest = VSyncReactorTest;
-TEST_F(VSyncReactorDeathTest, invalidRemoval) {
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.removeEventListener(&outerCb, &lastCallbackTime);
- EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
-}
-
-TEST_F(VSyncReactorDeathTest, invalidChange) {
- EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
-
- // the current DispSync-interface usage pattern has evolved around an implementation quirk,
- // which is a callback is assumed to always exist, and it is valid api usage to change the
- // offset of an object that is in the removed state.
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.removeEventListener(&outerCb, &lastCallbackTime);
- mReactor.changePhaseOffset(&outerCb, mPhase);
-}
-
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
- ON_CALL(*mMockDispatch, schedule(_, _, _))
- .WillByDefault(Return(ScheduleResult::CannotSchedule));
- EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
-}
-
-TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
- EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
- .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
- EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
-
- mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- ASSERT_TRUE(innerCb);
- Mock::VerifyAndClearExpectations(mMockDispatch.get());
-
- ON_CALL(*mMockDispatch, schedule(_, _, _))
- .WillByDefault(Return(ScheduleResult::CannotSchedule));
- EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
-}
-
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 0780af1..251ab36 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -20,17 +20,13 @@
#include "mock/DisplayHardware/MockComposer.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
Composer::Composer() = default;
Composer::~Composer() = default;
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::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/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index c2c5072..1ba3c0f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -24,8 +24,7 @@
class GraphicBuffer;
-namespace Hwc2 {
-namespace mock {
+namespace Hwc2::mock {
using android::hardware::graphics::common::V1_0::ColorTransform;
using android::hardware::graphics::common::V1_0::Transform;
@@ -52,11 +51,9 @@
MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&));
- MOCK_METHOD0(isRemote, bool());
MOCK_METHOD0(resetCommands, void());
MOCK_METHOD0(executeCommands, Error());
MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
- MOCK_CONST_METHOD0(isUsingVrComposer, bool());
MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
MOCK_METHOD1(acceptDisplayChanges, Error(Display));
@@ -110,7 +107,6 @@
MOCK_METHOD3(setLayerVisibleRegion,
Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t));
- MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
MOCK_METHOD4(getDisplayedContentSamplingAttributes,
@@ -143,6 +139,5 @@
MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
};
-} // namespace mock
-} // namespace Hwc2
+} // namespace Hwc2::mock
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
index 2ec37c1..c9788af 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -16,14 +16,10 @@
#include "mock/DisplayHardware/MockDisplay.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
Display::Display() = default;
Display::~Display() = default;
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
\ No newline at end of file
+} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index fe99e77..a96d9db 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -22,9 +22,7 @@
using android::HWC2::Layer;
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
namespace hal = android::hardware::graphics::composer::hal;
@@ -98,6 +96,4 @@
MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
};
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
index 8be7077..1ba38a8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
@@ -16,14 +16,10 @@
#include "MockPowerAdvisor.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
PowerAdvisor::PowerAdvisor() = default;
PowerAdvisor::~PowerAdvisor() = default;
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // 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 e22d0cf..7450b5d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -20,9 +20,7 @@
#include "DisplayHardware/PowerAdvisor.h"
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
class PowerAdvisor : public android::Hwc2::PowerAdvisor {
public:
@@ -34,6 +32,4 @@
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
deleted file mode 100644
index 1c8c44d..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
+++ /dev/null
@@ -1,64 +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.
- */
-
-#include "mock/MockDispSync.h"
-#include <thread>
-
-using namespace std::chrono_literals;
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-DispSync::DispSync() = default;
-DispSync::~DispSync() = default;
-
-status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback,
- nsecs_t /*lastCallbackTime*/) {
- if (mCallback.callback != nullptr) {
- return BAD_VALUE;
- }
-
- mCallback = {callback, phase};
- return NO_ERROR;
-}
-status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) {
- if (mCallback.callback != callback) {
- return BAD_VALUE;
- }
-
- mCallback = {nullptr, 0};
- return NO_ERROR;
-}
-
-status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
- if (mCallback.callback != callback) {
- return BAD_VALUE;
- }
-
- mCallback.phase = phase;
- return NO_ERROR;
-}
-
-void DispSync::triggerCallback() {
- if (mCallback.callback == nullptr) return;
-
- const std::chrono::nanoseconds now = std::chrono::steady_clock::now().time_since_epoch();
- const auto expectedVSyncTime = now + 16ms;
- mCallback.callback->onDispSyncEvent(now.count(), expectedVSyncTime.count());
-}
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
deleted file mode 100644
index b39487c..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ /dev/null
@@ -1,64 +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 "Scheduler/DispSync.h"
-
-namespace android {
-namespace mock {
-
-class DispSync : public android::DispSync {
-public:
- DispSync();
- ~DispSync() override;
-
- MOCK_METHOD0(reset, void());
- MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
- MOCK_METHOD0(beginResync, void());
- MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
- MOCK_METHOD0(endResync, void());
- MOCK_METHOD1(setPeriod, void(nsecs_t));
- MOCK_METHOD0(getPeriod, nsecs_t());
- MOCK_METHOD0(getIntendedPeriod, nsecs_t());
- MOCK_METHOD1(setRefreshSkipCount, void(int));
- MOCK_CONST_METHOD2(computeNextRefresh, nsecs_t(int, nsecs_t));
- MOCK_METHOD1(setIgnorePresentFences, void(bool));
- MOCK_METHOD1(expectedPresentTime, nsecs_t(nsecs_t));
-
- MOCK_CONST_METHOD1(dump, void(std::string&));
-
- status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
- nsecs_t lastCallbackTime) override;
- status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override;
- status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
-
- nsecs_t getCallbackPhase() { return mCallback.phase; }
-
- void triggerCallback();
-
-private:
- struct CallbackType {
- Callback* callback = nullptr;
- nsecs_t phase;
- };
- CallbackType mCallback;
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
new file mode 100644
index 0000000..cfc37ea
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "DisplayIdGenerator.h"
+
+namespace android::mock {
+
+template <typename T>
+class DisplayIdGenerator : public android::DisplayIdGenerator<T> {
+public:
+ // Explicit default instantiation is recommended.
+ DisplayIdGenerator() = default;
+ virtual ~DisplayIdGenerator() = default;
+
+ MOCK_METHOD0(nextId, std::optional<T>());
+ MOCK_METHOD1(markUnused, void(T));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
index 408cd35..302dc01 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
@@ -16,12 +16,10 @@
#include "mock/MockEventThread.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
EventThread::EventThread() = default;
EventThread::~EventThread() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 054aaf8..b4594c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -20,8 +20,7 @@
#include "Scheduler/EventThread.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class EventThread : public android::EventThread {
public:
@@ -35,7 +34,9 @@
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
MOCK_CONST_METHOD1(dump, void(std::string&));
- MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
+ MOCK_METHOD2(setDuration,
+ void(std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration));
MOCK_METHOD1(registerDisplayEventConnection,
status_t(const sp<android::EventThreadConnection> &));
MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
@@ -45,5 +46,4 @@
MOCK_METHOD0(getEventThreadConnectionCount, size_t());
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
index 358dfdb..417dcb0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
@@ -16,12 +16,10 @@
#include "mock/MockFrameTracer.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
FrameTracer::FrameTracer() = default;
FrameTracer::~FrameTracer() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
index f768b81..305cb1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
@@ -20,8 +20,7 @@
#include "FrameTracer/FrameTracer.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class FrameTracer : public android::FrameTracer {
public:
@@ -39,5 +38,4 @@
MOCK_METHOD0(miniDump, std::string());
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index a82b583..efaa9fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,6 +18,7 @@
#include <gmock/gmock.h>
+#include "FrameTimeline.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/MessageQueue.h"
@@ -34,6 +35,10 @@
MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
MOCK_METHOD0(invalidate, void());
MOCK_METHOD0(refresh, void());
+ MOCK_METHOD3(initVsync,
+ void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds));
+ MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
index 7e925b9..0a0e7b5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
@@ -20,15 +20,13 @@
#include "mock/MockSurfaceInterceptor.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
SurfaceInterceptor::SurfaceInterceptor() = default;
SurfaceInterceptor::~SurfaceInterceptor() = default;
-} // namespace mock
-} // namespace android
+} // 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
index ccb7bb9..b085027 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -20,8 +20,7 @@
#include "SurfaceInterceptor.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class SurfaceInterceptor : public android::SurfaceInterceptor {
public:
@@ -33,10 +32,12 @@
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
MOCK_METHOD0(disable, void());
MOCK_METHOD0(isEnabled, bool());
- MOCK_METHOD6(saveTransaction,
+ 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));
+ 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));
@@ -46,5 +47,4 @@
MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
index d686939..f8e76b2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -16,12 +16,10 @@
#include "mock/MockTimeStats.h"
-namespace android {
-namespace mock {
+namespace android::mock {
// Explicit default instantiation is recommended.
TimeStats::TimeStats() = default;
TimeStats::~TimeStats() = default;
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 4186e2b..ff37ec8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -20,8 +20,7 @@
#include "TimeStats/TimeStats.h"
-namespace android {
-namespace mock {
+namespace android::mock {
class TimeStats : public android::TimeStats {
public:
@@ -59,5 +58,4 @@
MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
};
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
similarity index 66%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index 10cf17c..bcccae5 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,5 +14,12 @@
* limitations under the License.
*/
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+#include "mock/MockVSyncTracker.h"
+
+namespace android::mock {
+
+// Explicit default instantiation is recommended.
+VSyncTracker::VSyncTracker() = default;
+VSyncTracker::~VSyncTracker() = default;
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
new file mode 100644
index 0000000..03ddc85
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <gmock/gmock.h>
+
+#include "Scheduler/VSyncTracker.h"
+
+namespace android::mock {
+
+class VSyncTracker : public android::scheduler::VSyncTracker {
+public:
+ VSyncTracker();
+ ~VSyncTracker() override;
+
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+ MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+ MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/libs/gui/GuiConfig.cpp b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
similarity index 61%
rename from libs/gui/GuiConfig.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
index 3ec20ee..25ae1bd 100644
--- a/libs/gui/GuiConfig.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * 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.
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-#include <gui/GuiConfig.h>
+#include "mock/MockVsyncController.h"
+#include <thread>
-namespace android {
+using namespace std::chrono_literals;
+namespace android::mock {
-void appendGuiConfigString(std::string& configStr) {
- static const char* config =
- " [libgui"
-#ifdef DONT_USE_FENCE_SYNC
- " DONT_USE_FENCE_SYNC"
-#endif
- "]";
- configStr.append(config);
-}
+// Explicit default instantiation is recommended.
+VsyncController::VsyncController() = default;
+VsyncController::~VsyncController() = default;
-}; // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
new file mode 100644
index 0000000..94d9966
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -0,0 +1,38 @@
+/*
+ * 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 "Scheduler/VsyncController.h"
+
+namespace android::mock {
+
+class VsyncController : public android::scheduler::VsyncController {
+public:
+ VsyncController();
+ ~VsyncController() override;
+
+ MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
+ MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
+ MOCK_METHOD1(setIgnorePresentFences, void(bool));
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 081d18b..a13f93b 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,8 +15,10 @@
*/
#pragma once
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/Rect.h>
#include <utils/String8.h>
+#include <functional>
#include "TransactionUtils.h"
namespace android {
@@ -27,18 +29,24 @@
// individual pixel values for testing purposes.
class ScreenCapture : public RefBase {
public:
- static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
- captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
- }
-
- static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
- const DisplayCaptureArgs& captureArgs) {
+ static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
const auto sf = ComposerService::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureDisplay(captureArgs, captureResults));
- *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
+ captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureDisplay(captureArgs, captureListener);
+
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+ captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
}
static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
@@ -47,13 +55,31 @@
captureDisplay(sc, args);
}
- static void captureLayers(std::unique_ptr<ScreenCapture>* sc,
- const LayerCaptureArgs& captureArgs) {
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
+ DisplayCaptureArgs& captureArgs) {
+ ScreenCaptureResults captureResults;
+ ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
+ *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
+ }
+
+ static status_t captureLayers(LayerCaptureArgs& captureArgs,
+ ScreenCaptureResults& captureResults) {
+ const auto sf = ComposerService::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
+ captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t status = sf->captureLayers(captureArgs, captureListener);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ captureResults = captureListener->waitForResults();
+ return captureResults.result;
+ }
+
+ static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) {
ScreenCaptureResults captureResults;
- ASSERT_EQ(NO_ERROR, sf->captureLayers(captureArgs, captureResults));
+ ASSERT_EQ(NO_ERROR, captureLayers(captureArgs, captureResults));
*sc = std::make_unique<ScreenCapture>(captureResults.buffer);
}
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index c45a1a1..fa742c5 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -19,6 +19,7 @@
"VibratorCallbackScheduler.cpp",
"VibratorHalController.cpp",
"VibratorHalWrapper.cpp",
+ "VibratorManagerHalWrapper.cpp",
],
aidl: {
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
index 3f8cd67..f2870b0 100644
--- a/services/vibratorservice/VibratorCallbackScheduler.cpp
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -71,14 +71,17 @@
void CallbackScheduler::loop() {
while (true) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mMutex);
if (mFinished) {
// Destructor was called, so let the callback thread die.
break;
}
while (!mQueue.empty() && mQueue.top().isExpired()) {
- mQueue.top().run();
+ DelayedCallback callback = mQueue.top();
mQueue.pop();
+ lock.unlock();
+ callback.run();
+ lock.lock();
}
if (mQueue.empty()) {
// Wait until a new callback is scheduled.
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 46175ad..e8606ca 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -46,8 +46,6 @@
// -------------------------------------------------------------------------------------------------
-static constexpr int MAX_RETRIES = 1;
-
std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) {
static bool gHalExists = true;
if (!gHalExists) {
@@ -89,6 +87,8 @@
// -------------------------------------------------------------------------------------------------
+static constexpr int MAX_RETRIES = 1;
+
template <typename T>
HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) {
if (result.isFailed()) {
@@ -126,11 +126,12 @@
// -------------------------------------------------------------------------------------------------
-void HalController::init() {
+bool HalController::init() {
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
if (mConnectedHal == nullptr) {
mConnectedHal = mHalConnector->connect(mCallbackScheduler);
}
+ return mConnectedHal != nullptr;
}
HalResult<void> HalController::ping() {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index ce20aeb..9672644 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -501,6 +501,10 @@
}
if (externalControlResult.withDefault(false)) {
capabilities |= Capabilities::EXTERNAL_CONTROL;
+
+ if (amplitudeResult.withDefault(false)) {
+ capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
+ }
}
return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
new file mode 100644
index 0000000..71955af
--- /dev/null
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapper"
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+namespace android {
+
+namespace vibrator {
+
+constexpr int32_t SINGLE_VIBRATOR_ID = 0;
+
+HalResult<void> LegacyManagerHalWrapper::ping() {
+ return mController->ping();
+}
+
+void LegacyManagerHalWrapper::tryReconnect() {
+ mController->tryReconnect();
+}
+
+HalResult<std::vector<int32_t>> LegacyManagerHalWrapper::getVibratorIds() {
+ if (mController->init()) {
+ return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>(1, SINGLE_VIBRATOR_ID));
+ }
+ // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+ return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>());
+}
+
+HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(int32_t id) {
+ if (id == SINGLE_VIBRATOR_ID && mController->init()) {
+ return HalResult<std::shared_ptr<HalController>>::ok(mController);
+ }
+ // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator.
+ return HalResult<std::shared_ptr<HalController>>::failed("No vibrator with id = " +
+ std::to_string(id));
+}
+
+HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) {
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> LegacyManagerHalWrapper::cancelSynced() {
+ return HalResult<void>::unsupported();
+}
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
new file mode 100644
index 0000000..c1a03a1
--- /dev/null
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+
+cc_benchmark {
+ name: "libvibratorservice_benchmarks",
+ srcs: [
+ "VibratorHalControllerBenchmarks.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "libvibratorservice",
+ "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..0d4c55e
--- /dev/null
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -0,0 +1,398 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PowerHalControllerBenchmarks"
+
+#include <benchmark/benchmark.h>
+#include <vibratorservice/VibratorHalController.h>
+
+using ::android::enum_range;
+using ::benchmark::Counter;
+using ::benchmark::Fixture;
+using ::benchmark::kMicrosecond;
+using ::benchmark::State;
+using ::benchmark::internal::Benchmark;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+class VibratorBench : public Fixture {
+public:
+ void SetUp(State& /*state*/) override { mController.init(); }
+
+ void TearDown(State& /*state*/) override { mController.off(); }
+
+ static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
+
+ static void DefaultArgs(Benchmark* /*b*/) {
+ // none
+ }
+
+protected:
+ vibrator::HalController mController;
+
+ auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
+
+ bool hasCapabilities(vibrator::HalResult<vibrator::Capabilities> result,
+ vibrator::Capabilities query) {
+ if (!result.isOk()) {
+ return false;
+ }
+ return (result.value() & query) == query;
+ }
+};
+
+#define BENCHMARK_WRAPPER(fixt, test, code) \
+ BENCHMARK_DEFINE_F(fixt, test) \
+ /* NOLINTNEXTLINE */ \
+ (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \
+ ->Apply(fixt::DefaultConfig) \
+ ->Apply(fixt::DefaultArgs)
+
+BENCHMARK_WRAPPER(VibratorBench, init, {
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ state.ResumeTiming();
+ controller.init();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, initCached, {
+ for (auto _ : state) {
+ mController.init();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, ping, {
+ for (auto _ : state) {
+ mController.ping();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
+ for (auto _ : state) {
+ mController.tryReconnect();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, on, {
+ auto duration = 60s;
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ mController.on(duration, callback);
+ state.PauseTiming();
+ mController.off();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, off, {
+ auto duration = 60s;
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ mController.on(duration, callback);
+ state.ResumeTiming();
+ mController.off();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+ return;
+ }
+
+ auto duration = 60s;
+ auto callback = []() {};
+ auto amplitude = UINT8_MAX;
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ controller.on(duration, callback);
+ state.ResumeTiming();
+ controller.setAmplitude(amplitude);
+ state.PauseTiming();
+ controller.off();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+ return;
+ }
+
+ auto duration = 6000s;
+ auto callback = []() {};
+ auto amplitude = UINT8_MAX;
+
+ mController.on(duration, callback);
+
+ for (auto _ : state) {
+ mController.setAmplitude(amplitude);
+ }
+
+ mController.off();
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+ return;
+ }
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ state.ResumeTiming();
+ controller.setExternalControl(true);
+ state.PauseTiming();
+ controller.setExternalControl(false);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+ return;
+ }
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ mController.setExternalControl(true);
+ state.PauseTiming();
+ mController.setExternalControl(false);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL)) {
+ return;
+ }
+
+ auto amplitude = UINT8_MAX;
+
+ mController.setExternalControl(true);
+
+ for (auto _ : state) {
+ mController.setAmplitude(amplitude);
+ }
+
+ mController.setExternalControl(false);
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ state.ResumeTiming();
+ controller.getCapabilities();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
+ // First call to cache values.
+ mController.getCapabilities();
+
+ for (auto _ : state) {
+ mController.getCapabilities();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ state.ResumeTiming();
+ controller.getSupportedEffects();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
+ // First call to cache values.
+ mController.getSupportedEffects();
+
+ for (auto _ : state) {
+ mController.getSupportedEffects();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
+ for (auto _ : state) {
+ state.PauseTiming();
+ vibrator::HalController controller;
+ controller.init();
+ state.ResumeTiming();
+ controller.getSupportedPrimitives();
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
+ // First call to cache values.
+ mController.getSupportedPrimitives();
+
+ for (auto _ : state) {
+ mController.getSupportedPrimitives();
+ }
+});
+
+class VibratorEffectsBench : public VibratorBench {
+public:
+ static void DefaultArgs(Benchmark* b) {
+ vibrator::HalController controller;
+ auto effectsResult = controller.getSupportedEffects();
+ if (!effectsResult.isOk()) {
+ return;
+ }
+
+ std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+ b->ArgNames({"Effect", "Strength"});
+ for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
+ if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+ continue;
+ }
+ for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) {
+ b->Args({static_cast<long>(effect), static_cast<long>(strength)});
+ }
+ }
+ }
+
+protected:
+ auto getEffect(const State& state) const {
+ return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
+ }
+
+ auto getStrength(const State& state) const {
+ return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1));
+ }
+};
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+ return;
+ }
+
+ int32_t id = 1;
+ auto effect = getEffect(state);
+ auto strength = getStrength(state);
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ mController.alwaysOnEnable(id, effect, strength);
+ state.PauseTiming();
+ mController.alwaysOnDisable(id);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+ return;
+ }
+
+ int32_t id = 1;
+ auto effect = getEffect(state);
+ auto strength = getStrength(state);
+
+ for (auto _ : state) {
+ state.PauseTiming();
+ mController.alwaysOnEnable(id, effect, strength);
+ state.ResumeTiming();
+ mController.alwaysOnDisable(id);
+ }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+ auto effect = getEffect(state);
+ auto strength = getStrength(state);
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ mController.performEffect(effect, strength, callback);
+ state.PauseTiming();
+ mController.off();
+ }
+});
+
+class VibratorPrimitivesBench : public VibratorBench {
+public:
+ static void DefaultArgs(Benchmark* b) {
+ vibrator::HalController controller;
+ auto primitivesResult = controller.getSupportedPrimitives();
+ if (!primitivesResult.isOk()) {
+ return;
+ }
+
+ std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+ b->ArgNames({"Primitive"});
+ for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+ if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+ continue;
+ }
+ b->Args({static_cast<long>(primitive)});
+ }
+ }
+
+protected:
+ auto getPrimitive(const State& state) const {
+ return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+ }
+};
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
+ auto capabilitiesResult = mController.getCapabilities();
+
+ if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::COMPOSE_EFFECTS)) {
+ return;
+ }
+
+ hardware::vibrator::CompositeEffect effect;
+ effect.primitive = getPrimitive(state);
+ effect.scale = 1.0f;
+ effect.delayMs = 0;
+
+ std::vector<hardware::vibrator::CompositeEffect> effects;
+ effects.push_back(effect);
+ auto callback = []() {};
+
+ for (auto _ : state) {
+ state.ResumeTiming();
+ mController.performComposedEffect(effects, callback);
+ state.PauseTiming();
+ mController.off();
+ }
+});
+
+BENCHMARK_MAIN();
\ No newline at end of file
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 3b61f42..d1028a4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -51,11 +51,19 @@
mConnectedHal(nullptr) {}
virtual ~HalController() = default;
- void init();
+ /* Connects to the newest HAL version available, possibly waiting for the registered service to
+ * become available. This will automatically be called at the first API usage if it was not
+ * manually called beforehand. Calling this manually during the setup phase can avoid slowing
+ * the first API call later on. Returns true if any HAL version is available, false otherwise.
+ */
+ virtual bool init();
- HalResult<void> ping() final override;
- void tryReconnect() final override;
+ /* reloads HAL service instance without waiting. This relies on the HAL version found by init()
+ * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called.
+ */
+ virtual void tryReconnect() override;
+ virtual HalResult<void> ping() override;
HalResult<void> on(std::chrono::milliseconds timeout,
const std::function<void()>& completionCallback) final override;
HalResult<void> off() final override;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 7b99bbb..bcb735d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -140,9 +140,12 @@
: mCallbackScheduler(std::move(scheduler)) {}
virtual ~HalWrapper() = default;
- virtual HalResult<void> ping() = 0;
+ /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+ * service restarts, to rapidly retry after a failure.
+ */
virtual void tryReconnect() = 0;
+ virtual HalResult<void> ping() = 0;
virtual HalResult<void> on(std::chrono::milliseconds timeout,
const std::function<void()>& completionCallback) = 0;
virtual HalResult<void> off() = 0;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
new file mode 100644
index 0000000..99947a5
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+#define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
+
+#include <vibratorservice/VibratorHalController.h>
+
+namespace android {
+
+namespace vibrator {
+
+// Wrapper for VibratorManager HAL handlers.
+class ManagerHalWrapper {
+public:
+ ManagerHalWrapper() = default;
+ virtual ~ManagerHalWrapper() = default;
+
+ virtual HalResult<void> ping() = 0;
+
+ /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the
+ * service restarts, to rapidly retry after a failure.
+ */
+ virtual void tryReconnect() = 0;
+
+ virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0;
+ virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0;
+
+ virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0;
+ virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0;
+ virtual HalResult<void> cancelSynced() = 0;
+};
+
+// Wrapper for the VibratorManager over single Vibrator HAL.
+class LegacyManagerHalWrapper : public ManagerHalWrapper {
+public:
+ LegacyManagerHalWrapper() : LegacyManagerHalWrapper(std::make_shared<HalController>()) {}
+ explicit LegacyManagerHalWrapper(std::shared_ptr<HalController> controller)
+ : mController(std::move(controller)) {}
+ virtual ~LegacyManagerHalWrapper() = default;
+
+ HalResult<void> ping() override final;
+ void tryReconnect() override final;
+
+ HalResult<std::vector<int32_t>> getVibratorIds() override final;
+ HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final;
+
+ HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final;
+ HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final;
+ HalResult<void> cancelSynced() override final;
+
+private:
+ const std::shared_ptr<HalController> mController;
+};
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index 9033124..5fc6d45 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -23,6 +23,7 @@
"VibratorHalWrapperHidlV1_1Test.cpp",
"VibratorHalWrapperHidlV1_2Test.cpp",
"VibratorHalWrapperHidlV1_3Test.cpp",
+ "VibratorManagerHalWrapperLegacyTest.cpp",
],
cflags: [
"-Wall",
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index f04e016..cda5e9a 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -184,11 +184,11 @@
// -------------------------------------------------------------------------------------------------
TEST_F(VibratorHalControllerTest, TestInit) {
- mController->init();
+ ASSERT_TRUE(mController->init());
ASSERT_EQ(1, mConnectCounter);
// Noop when wrapper was already initialized.
- mController->init();
+ ASSERT_TRUE(mController->init());
ASSERT_EQ(1, mConnectCounter);
}
@@ -339,6 +339,7 @@
std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr);
ASSERT_EQ(0, mConnectCounter);
+ ASSERT_FALSE(mController->init());
ASSERT_TRUE(mController->ping().isUnsupported());
ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported());
ASSERT_TRUE(mController->off().isUnsupported());
@@ -356,7 +357,7 @@
.isUnsupported());
// One connection attempt per api call.
- ASSERT_EQ(12, mConnectCounter);
+ ASSERT_EQ(13, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index 5de6257..08652f4 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -118,7 +118,8 @@
auto result = mWrapper->getCapabilities();
ASSERT_TRUE(result.isOk());
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+ ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
+ vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
result.value());
}
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
new file mode 100644
index 0000000..d5520a1
--- /dev/null
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VibratorManagerHalWrapperLegacyTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+using android::hardware::vibrator::CompositeEffect;
+using android::hardware::vibrator::CompositePrimitive;
+using android::hardware::vibrator::Effect;
+using android::hardware::vibrator::EffectStrength;
+
+using std::chrono::milliseconds;
+
+using namespace android;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockHalController : public vibrator::HalController {
+public:
+ MockHalController() = default;
+ virtual ~MockHalController() = default;
+
+ MOCK_METHOD(bool, init, (), (override));
+ MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override));
+ MOCK_METHOD(void, tryReconnect, (), (override));
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorManagerHalWrapperLegacyTest : public Test {
+public:
+ void SetUp() override {
+ mMockController = std::make_shared<StrictMock<MockHalController>>();
+ mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController);
+ ASSERT_NE(mWrapper, nullptr);
+ }
+
+protected:
+ std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr;
+ std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) {
+ EXPECT_CALL(*mMockController.get(), ping())
+ .Times(Exactly(2))
+ .WillOnce(Return(vibrator::HalResult<void>::failed("message")))
+ .WillRepeatedly(Return(vibrator::HalResult<void>::ok()));
+
+ ASSERT_TRUE(mWrapper->ping().isFailed());
+ ASSERT_TRUE(mWrapper->ping().isOk());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) {
+ EXPECT_CALL(*mMockController.get(), tryReconnect()).Times(Exactly(1));
+
+ mWrapper->tryReconnect();
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) {
+ std::vector<int32_t> expectedIds;
+ expectedIds.push_back(0);
+
+ EXPECT_CALL(*mMockController.get(), init())
+ .Times(Exactly(2))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+
+ auto result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(std::vector<int32_t>(), result.value());
+
+ result = mWrapper->getVibratorIds();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(expectedIds, result.value());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithValidIdReturnsController) {
+ EXPECT_CALL(*mMockController.get(), init())
+ .Times(Exactly(2))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+
+ ASSERT_TRUE(mWrapper->getVibrator(0).isFailed());
+
+ auto result = mWrapper->getVibrator(0);
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(mMockController.get(), result.value().get());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithInvalidIdFails) {
+ ASSERT_TRUE(mWrapper->getVibrator(-1).isFailed());
+}
+
+TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) {
+ std::vector<int32_t> vibratorIds;
+ vibratorIds.push_back(0);
+
+ ASSERT_TRUE(mWrapper->prepareSynced(vibratorIds).isUnsupported());
+ ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported());
+ ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported());
+}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0f791c9..48090af 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -258,7 +258,11 @@
bool shared;
struct Image {
- Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
+ Image()
+ : image(VK_NULL_HANDLE),
+ dequeue_fence(-1),
+ release_fence(-1),
+ dequeued(false) {}
VkImage image;
android::sp<ANativeWindowBuffer> buffer;
// The fence is only valid when the buffer is dequeued, and should be
@@ -266,6 +270,10 @@
// closed: either by closing it explicitly when queueing the buffer,
// or by passing ownership e.g. to ANativeWindow::cancelBuffer().
int dequeue_fence;
+ // This fence is a dup of the sync fd returned from the driver via
+ // vkQueueSignalReleaseImageANDROID upon vkQueuePresentKHR. We must
+ // ensure it is closed upon re-presenting or releasing the image.
+ int release_fence;
bool dequeued;
} images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
@@ -280,10 +288,19 @@
return reinterpret_cast<Swapchain*>(handle);
}
+static bool IsFencePending(int fd) {
+ if (fd < 0)
+ return false;
+
+ errno = 0;
+ return sync_wait(fd, 0 /* timeout */) == -1 && errno == ETIME;
+}
+
void ReleaseSwapchainImage(VkDevice device,
ANativeWindow* window,
int release_fence,
- Swapchain::Image& image) {
+ Swapchain::Image& image,
+ bool defer_if_pending) {
ATRACE_CALL();
ALOG_ASSERT(release_fence == -1 || image.dequeued,
@@ -319,10 +336,18 @@
close(release_fence);
}
}
-
+ release_fence = -1;
image.dequeued = false;
}
+ if (defer_if_pending && IsFencePending(image.release_fence))
+ return;
+
+ if (image.release_fence >= 0) {
+ close(image.release_fence);
+ image.release_fence = -1;
+ }
+
if (image.image) {
ATRACE_BEGIN("DestroyImage");
GetData(device).driver.DestroyImage(device, image.image, nullptr);
@@ -338,7 +363,8 @@
return;
for (uint32_t i = 0; i < swapchain->num_images; i++) {
if (!swapchain->images[i].dequeued)
- ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+ ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i],
+ true);
}
swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
swapchain->timing.clear();
@@ -996,7 +1022,7 @@
}
for (uint32_t i = 0; i < swapchain->num_images; i++) {
- ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+ ReleaseSwapchainImage(device, window, -1, swapchain->images[i], false);
}
if (active) {
@@ -1628,6 +1654,9 @@
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]) {
@@ -1761,7 +1790,7 @@
WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
}
} else {
- ReleaseSwapchainImage(device, nullptr, fence, img);
+ ReleaseSwapchainImage(device, nullptr, fence, img, true);
swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
}