Merge "fillInputInfo: Guard against integer overflow."
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/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..d108e00 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(10).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());
}
@@ -2653,15 +2773,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 +2884,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 +3046,7 @@
return;
}
dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_);
+ zip_entry_tasks_ = std::make_unique<TaskQueue>();
}
void Dumpstate::ShutdownDumpPool() {
@@ -2943,6 +3054,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 +3798,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 +3814,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 +3941,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/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/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 1572e3b..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,35 +66,33 @@
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) {
PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
- PrepareInputFileFlags(zip_fd, oat_fd, input_vdex_fd, output_vdex_fd, image_fd, input_file_name,
- output_file_name, profile_fd, dex_metadata_fd, swap_fd,
- class_loader_context, class_loader_context_fds);
+ PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
+ dex_metadata, profile, swap_fd, class_loader_context,
+ class_loader_context_fds);
- PrepareCompilerConfigFlags(input_vdex_fd, output_vdex_fd, instruction_set, compiler_filter,
+ PrepareCompilerConfigFlags(input_vdex, output_vdex, instruction_set, compiler_filter,
debuggable, target_sdk_version, enable_hidden_api_checks,
generate_compact_dex, compilation_reason);
@@ -139,38 +139,36 @@
AddArg(updatable_bcp_packages);
}
-void RunDex2Oat::PrepareInputFileFlags(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,
- int profile_fd,
- int dex_metadata_fd,
+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_file_name);
- ALOGV("Running %s in=%s out=%s\n", dex2oat_bin_.c_str(), input_basename.c_str(),
- output_file_name);
+ 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", zip_fd));
+ AddArg(StringPrintf("--zip-fd=%d", input_dex.fd()));
AddArg(StringPrintf("--zip-location=%s", input_basename.c_str()));
- AddArg(StringPrintf("--oat-fd=%d", oat_fd));
- AddArg(StringPrintf("--oat-location=%s", output_file_name));
- AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex_fd));
- AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex_fd));
+ 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 (image_fd >= 0) {
- AddArg(StringPrintf("--app-image-fd=%d", image_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 (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 (profile.fd() != -1) {
+ AddArg(StringPrintf("--profile-file-fd=%d", profile.fd()));
}
if (swap_fd >= 0) {
AddArg(StringPrintf("--swap-fd=%d", swap_fd));
@@ -178,7 +176,7 @@
// Get the directory of the apk to pass as a base classpath directory.
{
- std::string apk_dir(input_file_name);
+ 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);
@@ -195,8 +193,8 @@
}
}
-void RunDex2Oat::PrepareCompilerConfigFlags(int input_vdex_fd,
- int output_vdex_fd,
+void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+ const UniqueFile& output_vdex,
const char* instruction_set,
const char* compiler_filter,
bool debuggable,
@@ -206,11 +204,11 @@
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);
+ 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));
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 5453272..325a3a2 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -25,31 +25,31 @@
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);
@@ -57,20 +57,18 @@
protected:
void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
- void PrepareInputFileFlags(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,
- int profile_fd,
- int dex_metadata_fd,
+ 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(int input_vdex_fd,
- int output_vdex_fd,
+ void PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+ const UniqueFile& output_vdex,
const char* instruction_set,
const char* compiler_filter,
bool debuggable,
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/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/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/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/input/Input.h b/include/input/Input.h
index d40ba43..9feab7b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -312,11 +312,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.
*/
@@ -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/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/PropertyMap.h b/include/input/PropertyMap.h
new file mode 100644
index 0000000..3d04331
--- /dev/null
+++ b/include/input/PropertyMap.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <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 status_t load(const String8& filename, PropertyMap** outMap);
+
+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/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..9675a53 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",
],
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..0c71ed8 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -679,7 +679,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 +860,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 +1320,26 @@
}
}
+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/Parcel.cpp b/libs/binder/Parcel.cpp
index 14ab60f..06f6249 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
@@ -2625,16 +2618,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 +2665,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 +2761,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 +2810,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 +2843,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/ProcessState.cpp b/libs/binder/ProcessState.cpp
index acc1e67..a530565 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));
}
@@ -397,14 +393,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/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..cdeccea 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -34,7 +34,21 @@
public:
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);
+
sp<ProcessState> process();
status_t clearLastError();
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..7898928 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -36,6 +36,37 @@
#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
+
+
#ifdef __cplusplus
} // namespace android
#endif
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 7d9fd51..51fd84c 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"
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/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..d889593 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -123,8 +123,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 +155,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/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/rust/Android.bp b/libs/binder/rust/Android.bp
index 16811ee..0234820 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,8 +28,8 @@
rust_bindgen {
name: "libbinder_ndk_bindgen",
crate_name: "binder_ndk_bindgen",
- wrapper_src: "BinderBindings.h",
- source_stem: "ndk_bindings",
+ wrapper_src: "sys/BinderBindings.h",
+ source_stem: "bindings",
cflags: [
"-x c++",
],
@@ -69,6 +81,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..ceb6ade 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<()>;
@@ -581,5 +584,56 @@
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))
+ }
+ }
+ };
+}
+
+/// 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..82212d8 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();
@@ -363,6 +384,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.h
similarity index 98%
rename from libs/binder/rust/BinderBindings.h
rename to libs/binder/rust/sys/BinderBindings.h
index c7a06d9..303f4a5 100644
--- a/libs/binder/rust/BinderBindings.h
+++ b/libs/binder/rust/sys/BinderBindings.h
@@ -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..145c099 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,40 @@
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));
+ 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 +1215,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/gui/Android.bp b/libs/gui/Android.bp
index 686e274..ae265ca 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,13 +55,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",
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 2c50acc..c7c75da 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -109,52 +109,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);
- status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureDisplay failed to transact: %d", result);
- return result;
- }
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- SAFE_PARCEL(captureResults.read, reply);
- return NO_ERROR;
+ 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());
- SAFE_PARCEL(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;
- }
+ SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
- SAFE_PARCEL(captureResults.read, reply);
- return NO_ERROR;
+ 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 = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("captureLayers failed to transact: %d", result);
- return result;
- }
-
- SAFE_PARCEL(captureResults.read, reply);
- return NO_ERROR;
+ return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply);
}
virtual bool authenticateSurfaceTexture(
@@ -263,10 +244,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)
@@ -1251,37 +1247,29 @@
case CAPTURE_DISPLAY: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayCaptureArgs args;
- ScreenCaptureResults captureResults;
-
+ sp<IScreenCaptureListener> captureListener;
SAFE_PARCEL(args.read, data);
- status_t res = captureDisplay(args, captureResults);
- if (res == NO_ERROR) {
- SAFE_PARCEL(captureResults.write, *reply);
- }
- return res;
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
+
+ return captureDisplay(args, captureListener);
}
case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
uint64_t displayOrLayerStack = 0;
+ sp<IScreenCaptureListener> captureListener;
SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
- ScreenCaptureResults captureResults;
- status_t res = captureDisplay(displayOrLayerStack, captureResults);
- if (res == NO_ERROR) {
- SAFE_PARCEL(captureResults.write, *reply);
- }
- return res;
+ 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);
- status_t res = captureLayers(args, captureResults);
- if (res == NO_ERROR) {
- SAFE_PARCEL(captureResults.write, *reply);
- }
- return res;
+ SAFE_PARCEL(data.readStrongBinder, &captureListener);
+
+ return captureLayers(args, captureListener);
}
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1322,10 +1310,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: {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 62f7c23..6ff4a3d 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -493,6 +493,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();
@@ -541,6 +549,7 @@
SAFE_PARCEL(output.writeFloat, frameScale);
SAFE_PARCEL(output.writeBool, captureSecureLayers);
SAFE_PARCEL(output.writeInt32, uid);
+ SAFE_PARCEL(output.writeBool, useRGBColorSpace);
return NO_ERROR;
}
@@ -552,7 +561,7 @@
SAFE_PARCEL(input.readFloat, &frameScale);
SAFE_PARCEL(input.readBool, &captureSecureLayers);
SAFE_PARCEL(input.readInt32, &uid);
-
+ SAFE_PARCEL(input.readBool, &useRGBColorSpace);
return NO_ERROR;
}
@@ -607,19 +616,31 @@
}
status_t ScreenCaptureResults::write(Parcel& output) const {
- SAFE_PARCEL(output.write, *buffer);
+ 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();
- SAFE_PARCEL(input.read, *buffer);
+ 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;
SAFE_PARCEL(input.readUint32, &dataspace);
capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+ SAFE_PARCEL(input.readInt32, &result);
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 62a3c45..6d1f399 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1365,11 +1365,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);
}
@@ -1923,24 +1925,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/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/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 7974a06..0d0d102 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -72,6 +72,7 @@
struct VSync {
uint32_t count;
nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
};
struct Hotplug {
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h
deleted file mode 100644
index 7aa5432..0000000
--- a/libs/gui/include/gui/GuiConfig.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_GUI_CONFIG_H
-#define ANDROID_GUI_CONFIG_H
-
-#include <string>
-
-namespace android {
-
-// Append the libgui configuration details to configStr.
-void appendGuiConfigString(std::string& configStr);
-
-}; // namespace android
-
-#endif /*ANDROID_GUI_CONFIG_H*/
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 e955ea8..e057b68 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -22,6 +22,7 @@
#include <binder/IBinder.h>
#include <binder/IInterface.h>
+#include <gui/IScreenCaptureListener.h>
#include <gui/ITransactionCompletedListener.h>
#include <math/vec4.h>
@@ -255,10 +256,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 {
@@ -271,7 +272,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.
*
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 653d849..a763d1d 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -311,6 +311,7 @@
// Merges the passed in commands and returns true if there were any changes.
bool merge(const InputWindowCommands& other);
+ bool empty() const;
void clear();
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
@@ -341,6 +342,12 @@
float frameScale{1};
bool captureSecureLayers{false};
int32_t uid{UNSET_UID};
+ // True to force using RGB color as the capture result.
+ // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+ // different from RGB (byte per color), and failed when checking colors.
+ // NOTE: This should only be used for testing since in normal cases, we want the screen
+ // capture's colorspace to match the display's colorspace
+ bool useRGBColorSpace{false};
virtual status_t write(Parcel& output) const;
virtual status_t read(const Parcel& input);
@@ -369,6 +376,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/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 1a9710a..6cac287 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -508,7 +508,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
@@ -595,14 +595,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/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/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index d88c477..da0d5f8 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>
@@ -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,
@@ -481,7 +496,7 @@
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/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 287a6f4..4d306e7 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;
@@ -156,7 +155,7 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- ~InputSurface() { mInputFlinger->unregisterInputChannel(*mServerChannel); }
+ ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel->getConnectionToken()); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
const sp<SurfaceControl>&)> transactionBody) {
@@ -176,6 +175,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;
@@ -281,7 +287,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 +302,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 +331,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 +343,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 +350,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 +362,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,12 +378,9 @@
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);
@@ -412,12 +399,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 +418,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 +434,6 @@
t.setTransparentRegionHint(sc, transparentRegion);
});
surface->showAt(100, 100);
- surface->assertFocusChange(true);
injectTap(101, 101);
surface->expectTap(1, 1);
}
@@ -466,10 +448,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 +465,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 +481,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 +498,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 +512,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 +523,17 @@
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);
}
-}
+} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 9fd8c42..dbede46 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) {
@@ -743,7 +758,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 +772,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; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 0c3c1f0..8f575a8 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -32,9 +32,11 @@
srcs: [
"Input.cpp",
"InputDevice.cpp",
+ "InputEventLabels.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "PropertyMap.cpp",
"TouchVideoFrame.cpp",
"VirtualKeyMap.cpp",
],
@@ -96,4 +98,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..dee240c
--- /dev/null
+++ b/libs/input/InputEventLabels.cpp
@@ -0,0 +1,448 @@
+/*
+ * 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), \
+ 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/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..4833eb9
--- /dev/null
+++ b/libs/input/PropertyMap.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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));
+ }
+}
+
+status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
+ *outMap = nullptr;
+
+ Tokenizer* tokenizer;
+ status_t status = Tokenizer::open(filename, &tokenizer);
+ if (status) {
+ ALOGE("Error %d opening property file %s.", status, filename.string());
+ } else {
+ PropertyMap* map = new PropertyMap();
+ if (!map) {
+ ALOGE("Error allocating property map.");
+ status = NO_MEMORY;
+ } else {
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ Parser parser(map, tokenizer);
+ 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) {
+ delete map;
+ } else {
+ *outMap = map;
+ }
+ }
+ delete tokenizer;
+ }
+ return status;
+}
+
+// --- 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..23ead0e
--- /dev/null
+++ b/libs/input/PropertyMap_fuzz.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 propertyMap) -> 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* mapPtr = &propertyMap;
+ android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+ },
+ [](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 proprtyMap = 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, proprtyMap);
+ }
+ return 0;
+}
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/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 5eefad3..0cdf0bb 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -31,7 +31,7 @@
oneway void setInputWindows(in InputWindowInfo[] inputHandles,
in @nullable ISetInputWindowsListener setInputWindowsListener);
void registerInputChannel(in InputChannel channel);
- void unregisterInputChannel(in InputChannel channel);
+ void unregisterInputChannel(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/tests/Android.bp b/libs/input/tests/Android.bp
index 9782c1a..7ff5ab6 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -19,13 +19,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/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/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3dcb498..1ccbff1 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,17 @@
],
}
+filegroup {
+ name: "librenderengine_skia_sources",
+ srcs: [
+ "skia/SkiaRenderEngine.cpp",
+ "skia/SkiaGLRenderEngine.cpp",
+ ],
+}
+
cc_library_static {
name: "librenderengine",
defaults: ["librenderengine_defaults"],
- vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
clang: true,
cflags: [
@@ -88,6 +93,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..c6436cd 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -22,6 +22,8 @@
#include "gl/GLESRenderEngine.h"
#include "threaded/RenderEngineThreaded.h"
+#include "skia/SkiaGLRenderEngine.h"
+
namespace android {
namespace renderengine {
@@ -37,12 +39,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");
@@ -62,10 +69,6 @@
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..3367664 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;
@@ -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);
@@ -622,13 +639,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 +656,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 +674,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 +687,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 +956,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 +964,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 +1304,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..c72b7f9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -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.
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 09a0f65..9b87229 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);
@@ -90,15 +90,8 @@
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 +109,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 +174,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,28 +263,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.
@@ -310,7 +271,6 @@
virtual ~RenderEngine() = 0;
bool useNativeFenceSync() const override;
- bool useWaitSync() const override;
protected:
RenderEngine(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index e03dd58..0b80d88 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -43,7 +43,6 @@
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>&));
@@ -61,6 +60,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..94ba153
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -0,0 +1,539 @@
+/*
+ * 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
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cmath>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <sync/sync.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
+
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkSurface.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;
+}
+
+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)
+ : renderengine::skia::SkiaRenderEngine(args),
+ mEGLDisplay(display),
+ mEGLConfig(config),
+ mEGLContext(ctxt),
+ mPlaceholderSurface(placeholder),
+ mProtectedEGLContext(protectedContext),
+ mProtectedPlaceholderSurface(protectedPlaceholder) {
+ // 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);
+}
+
+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);
+}
+
+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,
+ 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();
+
+ canvas->clipRect(SkRect::MakeLTRB(display.clip.left, display.clip.top, display.clip.right,
+ display.clip.bottom));
+ canvas->drawColor(0, SkBlendMode::kSrc);
+ for (const auto& layer : layers) {
+ if (layer->source.buffer.buffer) {
+ ATRACE_NAME("DrawImage");
+ const auto& item = layer->source.buffer;
+ 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);
+ mImageCache.insert({item.buffer->getId(), image});
+ }
+ const auto& bounds = layer->geometry.boundaries;
+ SkRect dest = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ canvas->drawImageRect(image, dest, nullptr);
+ } else {
+ ATRACE_NAME("DrawColor");
+ SkPaint paint;
+ const auto color = layer->source.solidColor;
+ paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
+ }
+ }
+ {
+ ATRACE_NAME("flush surface");
+ surface->flush();
+ }
+
+ 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;
+}
+
+size_t SkiaGLRenderEngine::getMaxTextureSize() const {
+ return mGrContext->maxTextureSize();
+}
+
+size_t SkiaGLRenderEngine::getMaxViewportDims() const {
+ return mGrContext->maxRenderTargetSize();
+}
+
+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..eb098cb
--- /dev/null
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <sys/types.h>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+#include <renderengine/RenderEngine.h>
+
+#include <GrDirectContext.h>
+#include <SkSurface.h>
+
+#include "SkiaRenderEngine.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);
+
+ base::unique_fd flush();
+ bool waitFence(base::unique_fd fenceFd);
+
+ EGLDisplay mEGLDisplay;
+ EGLConfig mEGLConfig;
+ EGLContext mEGLContext;
+ EGLSurface mPlaceholderSurface;
+ EGLContext mProtectedEGLContext;
+ EGLSurface mProtectedPlaceholderSurface;
+
+ // 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..3c5d0cf
--- /dev/null
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -0,0 +1,71 @@
+/*
+ * 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 impl::RenderEngine {
+public:
+ static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
+ SkiaRenderEngine(const RenderEngineCreationArgs& args) : RenderEngine(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 status_t bindExternalTextureBuffer(uint32_t /*texName*/,
+ const sp<GraphicBuffer>& /*buffer*/,
+ const sp<Fence>& /*fence*/) {
+ return 0;
+ }; // EXCLUDES(mRenderingMutex);
+ 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/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..953cbc6 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -131,21 +131,6 @@
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 +161,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 +191,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 +267,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 +304,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..ac9f606 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -46,16 +46,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 +63,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..ec8a78a 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -22,8 +22,7 @@
#include <ui/Transform.h>
#include <utils/String8.h>
-namespace android {
-namespace ui {
+namespace android::ui {
Transform::Transform() {
reset();
@@ -57,8 +56,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;
@@ -150,8 +148,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 +160,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 +168,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 +240,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 +271,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 +291,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 +311,7 @@
return out;
}
-uint32_t Transform::type() const
-{
+uint32_t Transform::type() const {
if (mType & UNKNOWN_TYPE) {
// recompute what this transform is
@@ -416,16 +406,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 +507,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 +523,4 @@
ALOGD("%s", out.c_str());
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
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/Transform.h b/libs/ui/include/ui/Transform.h
index 4c463bf..9a434e5 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
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/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 07ec327..a288c21 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,
@@ -170,7 +172,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 +207,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/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..8af9bcb 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -132,8 +132,8 @@
return binder::Status::ok();
}
-binder::Status InputManager::unregisterInputChannel(const InputChannel& channel) {
- mDispatcher->unregisterInputChannel(channel);
+binder::Status InputManager::unregisterInputChannel(const sp<IBinder>& connectionToken) {
+ mDispatcher->unregisterInputChannel(connectionToken);
return binder::Status::ok();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index bf86a98..35d2d58 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -109,7 +109,7 @@
const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
binder::Status registerInputChannel(const InputChannel& channel) override;
- binder::Status unregisterInputChannel(const InputChannel& channel) override;
+ binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
binder::Status setFocusedWindow(const FocusRequest&) override;
private:
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/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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 078448f..6460fe9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -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>
@@ -254,6 +252,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 +351,17 @@
}
}
-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");
+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";
}
- return key;
-}
-
-// --- 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 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;
- }
-
- if (hashLen != hash.size()) {
- ALOGE("HMAC-SHA256 has unexpected length");
- return INVALID_HMAC;
- }
-
- return hash;
}
// --- InputDispatcher ---
@@ -428,7 +401,7 @@
while (!mConnectionsByFd.empty()) {
sp<Connection> connection = mConnectionsByFd.begin()->second;
- unregisterInputChannel(*connection->inputChannel);
+ unregisterInputChannel(connection->inputChannel->getConnectionToken());
}
}
@@ -813,7 +786,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) {
@@ -1078,7 +1051,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 +1061,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
@@ -1171,10 +1144,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));
@@ -1463,8 +1436,7 @@
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);
@@ -1887,7 +1859,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();
@@ -2119,7 +2091,7 @@
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 +2107,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 +2144,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 +2481,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 +2655,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 +2680,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 +2690,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,
@@ -2836,7 +2815,7 @@
}
// Unregister the channel.
- d->unregisterInputChannelLocked(*connection->inputChannel, notify);
+ d->unregisterInputChannelLocked(connection->inputChannel->getConnectionToken(), notify);
return 0; // remove the callback
} // release lock
}
@@ -3356,7 +3335,6 @@
"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;
@@ -3559,7 +3537,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 +3545,7 @@
VerifiedMotionEvent verifiedMotionEvent =
verifiedMotionEventFromMotionEvent(motionEvent);
result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
- calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+ calculatedHmac = sign(verifiedMotionEvent);
break;
}
default: {
@@ -3639,9 +3617,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 +3631,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 +3641,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 +3813,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 =
@@ -3932,11 +3933,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 +3949,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());
}
}
}
@@ -4145,6 +4142,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 +4185,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");
@@ -4446,15 +4455,11 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannel(const InputChannel& inputChannel) {
-#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel.getName().c_str());
-#endif
-
+status_t InputDispatcher::unregisterInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
- status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
+ status_t status = unregisterInputChannelLocked(connectionToken, false /*notify*/);
if (status) {
return status;
}
@@ -4466,23 +4471,22 @@
return OK;
}
-status_t InputDispatcher::unregisterInputChannelLocked(const InputChannel& inputChannel,
+status_t InputDispatcher::unregisterInputChannelLocked(const sp<IBinder>& connectionToken,
bool notify) {
- sp<Connection> connection = getConnectionLocked(inputChannel.getConnectionToken());
+ 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 +4495,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 +4626,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;
@@ -5202,37 +5204,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..f3b3dda 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.
*
@@ -130,9 +121,11 @@
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 status_t unregisterInputChannel(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 +136,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 +179,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.
@@ -300,15 +301,24 @@
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 +327,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);
@@ -379,6 +391,14 @@
*/
std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(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.
// If a connection is not responsive, then the entries should not be added to the AnrTracker.
@@ -486,13 +506,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 unregisterInputChannelLocked(const sp<IBinder>& connectionToken, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -500,11 +521,10 @@
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 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 sp<Connection>& connection) REQUIRES(mLock);
void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 179a263..c3d50ea 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -176,7 +176,7 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual status_t unregisterInputChannel(const InputChannel& inputChannel) = 0;
+ virtual status_t unregisterInputChannel(const sp<IBinder>& connectionToken) = 0;
/* Allows an input monitor steal the current pointer stream away from normal input windows.
*
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..cabc782 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -29,8 +29,8 @@
#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 " "
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 2a2cea5..577309a 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -49,7 +49,7 @@
return binder::Status::ok();
}
binder::Status registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
- binder::Status unregisterInputChannel(const InputChannel&) { return binder::Status::ok(); }
+ binder::Status unregisterInputChannel(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..573524e 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -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..67d3cc2 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;
}
@@ -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..b224476 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -108,7 +108,7 @@
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));
@@ -240,7 +240,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 +481,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..feaacb3 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),
@@ -353,6 +354,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);
}
@@ -710,6 +723,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..6b28069 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>
@@ -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..2d6ccf5 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;
+ void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) 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;
@@ -157,6 +156,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);
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..b00e870 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -192,6 +192,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);
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index da50af5..6e08e1b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -294,70 +294,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 +328,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);
+ }
};
@@ -789,6 +737,8 @@
void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+ void setVisible(bool visible) { mInfo.visible = visible; }
+
void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
mInfo.dispatchingTimeout = timeout;
}
@@ -1225,80 +1175,6 @@
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 =
@@ -1523,6 +1399,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 +1602,7 @@
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -1833,6 +1712,8 @@
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
window->consumeFocusEvent(true);
FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
@@ -1926,6 +1807,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 +1820,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 +1832,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 +1847,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 +1914,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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_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,7 +2140,7 @@
mWindow->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
-
+ setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -2141,6 +2230,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 +2243,7 @@
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocusable(true);
mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+ setFocusedWindow(windowInSecondary);
windowInSecondary->consumeFocusEvent(true);
}
@@ -2270,6 +2361,23 @@
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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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 +2475,7 @@
// Expect one focus window exist in display.
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -2660,6 +2769,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+ setFocusedWindow(mWindow);
mWindow->consumeFocusEvent(true);
}
@@ -3053,6 +3163,7 @@
// Expect one focus window exist in display.
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
mFocusedWindow->consumeFocusEvent(true);
}
@@ -3249,6 +3360,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);
@@ -3386,4 +3498,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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ mMirror->setVisible(false);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ // single window is removed but the window token remains focused
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}});
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+
+ // Both windows are removed
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ mWindow->consumeFocusEvent(false);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_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(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+ ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_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..3aef1e4 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -148,7 +148,7 @@
const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
binder::Status registerInputChannel(const InputChannel& channel) override;
- binder::Status unregisterInputChannel(const InputChannel& channel) override;
+ binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
binder::Status setFocusedWindow(const FocusRequest&) override;
private:
@@ -220,13 +220,13 @@
return binder::Status::ok();
}
-binder::Status TestInputManager::unregisterInputChannel(const InputChannel& channel) {
+binder::Status TestInputManager::unregisterInputChannel(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);
}
@@ -380,7 +380,7 @@
ASSERT_EQ(channels.size(), 1UL);
EXPECT_EQ(channels[0], *serverChannel);
- mService->unregisterInputChannel(*serverChannel);
+ mService->unregisterInputChannel(serverChannel->getConnectionToken());
mQuery->getInputChannels(&channels);
EXPECT_EQ(channels.size(), 0UL);
}
@@ -397,15 +397,15 @@
EXPECT_EQ(channels.size(), 0UL);
mService->registerInputChannel(InputChannel());
- mService->unregisterInputChannel(*clientChannel);
+ mService->unregisterInputChannel(clientChannel->getConnectionToken());
mService->registerInputChannel(*serverChannel);
mService->registerInputChannel(*clientChannel);
mQuery->getInputChannels(&channels);
EXPECT_EQ(channels.size(), 2UL);
- mService->unregisterInputChannel(*clientChannel);
- mService->unregisterInputChannel(*serverChannel);
+ mService->unregisterInputChannel(clientChannel->getConnectionToken());
+ mService->unregisterInputChannel(serverChannel->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..4b0a945 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();
}
@@ -1827,7 +1736,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 +1751,7 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
}
- virtual void TearDown() override {
+ void TearDown() override {
ASSERT_EQ(mReader->stop(), OK);
mTestListener.clear();
mFakePolicy.clear();
@@ -1956,7 +1865,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 +2006,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 +2244,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 +2269,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 +2311,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 +2319,7 @@
event.code = code;
event.value = value;
mapper.process(&event);
+ mReader->loopOnce();
}
static void assertMotionRange(const InputDeviceInfo& info,
@@ -2543,10 +2461,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 +2565,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 +2583,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 +2602,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 +2807,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 +2865,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 +2911,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 +2980,78 @@
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);
+
+ 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, mapper.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 +3139,7 @@
std::shared_ptr<FakePointerController> mFakePointerController;
- virtual void SetUp() override {
+ void SetUp() override {
InputMapperTest::SetUp();
mFakePointerController = std::make_shared<FakePointerController>();
@@ -3211,7 +3241,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 +3869,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 +4287,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 +4337,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 +4458,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 +4533,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 +4629,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 +5524,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 +5800,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 +5975,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 +6955,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 +7473,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 +7652,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..d14a301 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -28,6 +28,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 +41,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;
@@ -445,6 +451,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)
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..8d071b5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -13,7 +13,10 @@
cc_defaults {
name: "libsurfaceflinger_defaults",
- defaults: ["surfaceflinger_defaults"],
+ defaults: [
+ "surfaceflinger_defaults",
+ "skia_deps",
+ ],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
"-DGL_GLEXT_PROTOTYPES",
@@ -68,6 +71,7 @@
},
static_libs: [
"libcompositionengine",
+ "libframetimeline",
"libperfetto_client_experimental",
"librenderengine",
"libserviceutils",
@@ -186,22 +190,18 @@
],
}
-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",
+cc_defaults {
+ name: "libsurfaceflinger_binary",
+ defaults: [
+ "surfaceflinger_defaults",
+ "libsurfaceflinger_production_defaults",
],
cflags: [
+ "-DLOG_TAG=\"SurfaceFlinger\"",
"-DUSE_VR_COMPOSER=1",
],
// VrComposer is not used when building surfaceflinger for vendors
+ // TODO: Is this ever built for vendors?
target: {
vendor: {
cflags: [
@@ -209,15 +209,6 @@
],
},
},
- logtags: ["EventLog/EventLogTags.logtags"],
-}
-
-cc_defaults {
- name: "libsurfaceflinger_binary",
- defaults: ["surfaceflinger_defaults"],
- cflags: [
- "-DLOG_TAG=\"SurfaceFlinger\"",
- ],
shared_libs: [
"android.frameworks.displayservice@1.0",
"android.hardware.configstore-utils",
@@ -239,23 +230,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.h b/services/surfaceflinger/BufferLayer.h
index 2483abb..d460d3d 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -50,10 +50,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 +115,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;
@@ -213,6 +175,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 +207,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..97ad212 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -229,10 +229,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
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5ebc22d..7d06128 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -35,10 +35,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 +51,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 +82,38 @@
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;
+
+ 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;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 884cc0c..ea1f78c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -508,13 +508,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 +552,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..81959ae 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;
@@ -117,6 +115,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 +129,6 @@
bool hasFrameUpdate() const override;
- status_t bindTextureImage() override;
status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) override;
@@ -141,8 +140,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.h b/services/surfaceflinger/Client.h
index e9063e5..f64be3a 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:
@@ -80,7 +76,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..b37ca33 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -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/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 8a9763b..b4ed92f 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
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 6cc7a53..6552c54 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -165,8 +165,7 @@
// 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;
+ const Rect& layerStackSpaceRect, const Rect& displaySpaceRect) = 0;
// Sets the bounds to use
virtual void setDisplaySpaceSize(const ui::Size&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 57b7a97..e009894 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -40,7 +40,7 @@
void setCompositionEnabled(bool) override;
void setProjection(const ui::Transform&, uint32_t orientation,
const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
- const Rect& displaySpaceRect, bool needsFiltering) override;
+ const Rect& displaySpaceRect) override;
void setDisplaySpaceSize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
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/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 375d334..5350611 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,8 +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_METHOD5(setProjection,
+ void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&));
MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 9e0a43a..816a09b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -107,7 +107,7 @@
void Output::setProjection(const ui::Transform& transform, uint32_t orientation,
const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
- const Rect& displaySpaceRect, bool needsFiltering) {
+ const Rect& displaySpaceRect) {
auto& outputState = editState();
outputState.transform = transform;
outputState.orientation = orientation;
@@ -123,7 +123,7 @@
outputState.layerStackSpace.content = layerStackSpaceRect;
outputState.layerStackSpace.bounds = layerStackSpaceRect;
- outputState.needsFiltering = needsFiltering;
+ outputState.needsFiltering = transform.needsBilinearFiltering();
dirtyEntireOutput();
}
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 2773fd3..8d1ffe3 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -42,6 +42,9 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+// Uncomment to enable RE-SK workarounds; b/b/168499446
+//#define USE_SKIA_WORKAROUNDS
+
namespace android::compositionengine {
RenderSurface::~RenderSurface() = default;
@@ -81,7 +84,11 @@
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);
+#ifdef USE_SKIA_WORKAROUNDS
+ status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE);
+#else
status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+#endif
ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
}
@@ -113,6 +120,9 @@
if (useProtected) {
usageFlags |= GRALLOC_USAGE_PROTECTED;
}
+#ifdef USE_SKIA_WORKAROUNDS
+ usageFlags |= GRALLOC_USAGE_HW_TEXTURE;
+#endif
const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
if (status == NO_ERROR) {
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/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 3dd26c0..23efd2d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -241,17 +241,14 @@
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;
- mOutput->setProjection(transform, orientation, frame, viewport, destinationClip,
- needsFiltering);
+ mOutput->setProjection(transform, orientation, frame, viewport, destinationClip);
EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().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);
}
/*
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index ea59692..b53f88d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -216,10 +216,6 @@
// 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;
@@ -233,7 +229,7 @@
getCompositionDisplay()->setProjection(globalTransform, transformOrientationFlags,
orientedDisplaySpaceRect, layerStackSpaceRect,
- displaySpaceRect, needsFiltering);
+ displaySpaceRect);
}
ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
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..c95440a
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 <cinttypes>
+
+namespace android::frametimeline::impl {
+
+using base::StringAppendF;
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+ 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) {}
+
+void SurfaceFrame::setPresentState(PresentState state) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPresentState = state;
+}
+
+PredictionState SurfaceFrame::getPredictionState() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPredictionState;
+}
+
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPresentState;
+}
+
+TimelineItem SurfaceFrame::getActuals() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mActuals;
+}
+
+void SurfaceFrame::setActuals(frametimeline::TimelineItem&& actuals) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals = actuals;
+}
+
+void SurfaceFrame::setPresentTime(nsecs_t presentTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals.presentTime = presentTime;
+}
+
+void SurfaceFrame::dump(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime);
+ StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime);
+ StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime);
+ StringAppendF(&result, "Predicted Render Complete Time : %" PRId64 "\n", mPredictions.endTime);
+ StringAppendF(&result, "Actual Render Complete Time : %" PRId64 "\n", mActuals.endTime);
+ StringAppendF(&result, "Predicted Present Time : %" PRId64 "\n", mPredictions.presentTime);
+ StringAppendF(&result, "Actual Present Time : %" PRId64 "\n", mActuals.presentTime);
+}
+
+FrameTimeline::FrameTimeline() : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()) {}
+
+FrameTimeline::DisplayFrame::DisplayFrame()
+ : surfaceFlingerPredictions(TimelineItem()),
+ surfaceFlingerActuals(TimelineItem()),
+ predictionState(PredictionState::None) {
+ this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
+}
+
+std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+ const std::string& layerName, std::optional<int64_t> token) {
+ 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) {
+ 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) {
+ 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->surfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
+ const std::shared_ptr<FenceTime>& presentFence) {
+ 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;
+ for (auto& surfaceFrame : displayFrame->surfaceFrames) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
+ // Only presented SurfaceFrames need to be updated
+ surfaceFrame->setPresentTime(signalTime);
+ }
+ }
+ }
+
+ mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
+ --i;
+ }
+}
+
+void FrameTimeline::finalizeCurrentDisplayFrame() {
+ while (mDisplayFrames.size() >= kMaxDisplayFrames) {
+ // 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>();
+}
+
+void FrameTimeline::dump(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+ for (const auto& displayFrame : mDisplayFrames) {
+ StringAppendF(&result, "---Display Frame---\n");
+ StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerPredictions.startTime);
+ StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerActuals.startTime);
+ StringAppendF(&result, "Predicted SF Complete time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerPredictions.endTime);
+ StringAppendF(&result, "Actual SF Complete time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerActuals.endTime);
+ StringAppendF(&result, "Predicted Present time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerPredictions.presentTime);
+ StringAppendF(&result, "Actual Present time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerActuals.presentTime);
+ for (size_t i = 0; i < displayFrame->surfaceFrames.size(); i++) {
+ StringAppendF(&result, "Surface frame - %" PRId32 "\n", (int)i);
+ displayFrame->surfaceFrames[i]->dump(result);
+ }
+ }
+}
+
+} // namespace android::frametimeline::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
new file mode 100644
index 0000000..291e30e
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -0,0 +1,232 @@
+/*
+ * 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 <ui/FenceTime.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+namespace android::frametimeline {
+
+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;
+};
+
+/*
+ * 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);
+};
+
+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() = 0;
+ virtual TimelineItem getActuals() = 0;
+ virtual PresentState getPresentState() = 0;
+ virtual PredictionState getPredictionState() = 0;
+
+ virtual void setPresentState(PresentState state) = 0;
+ virtual void setActuals(TimelineItem&& actuals) = 0;
+
+ // There is no prediction for Queue time and it is not a part of TimelineItem. Set it
+ // separately.
+ virtual void setActualQueueTime(nsecs_t actualQueueTime) = 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;
+};
+
+namespace impl {
+
+using namespace std::chrono_literals;
+
+class TokenManager : public android::frametimeline::TokenManager {
+public:
+ TokenManager() : mCurrentToken(0) {}
+ ~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() override { return mPredictions; };
+ TimelineItem getActuals() override;
+ PresentState getPresentState() override;
+ PredictionState getPredictionState() override;
+ void setActuals(TimelineItem&& actuals) override;
+ void setActualQueueTime(nsecs_t actualQueueTime) override {
+ mActualQueueTime = actualQueueTime;
+ };
+ void setPresentState(PresentState state) override;
+ void setPresentTime(nsecs_t presentTime);
+ void dump(std::string& result);
+
+private:
+ const std::string mLayerName;
+ PresentState mPresentState GUARDED_BY(mMutex);
+ PredictionState mPredictionState GUARDED_BY(mMutex);
+ const TimelineItem mPredictions;
+ TimelineItem mActuals GUARDED_BY(mMutex);
+ nsecs_t mActualQueueTime;
+ std::mutex mMutex;
+};
+
+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 dump(std::string& result);
+
+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;
+ };
+
+ void flushPendingPresentFences() REQUIRES(mMutex);
+ void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+
+ // 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;
+ static constexpr uint32_t kMaxDisplayFrames = 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;
+};
+
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8647a63..b19f7a5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1001,8 +1001,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) {
@@ -1453,6 +1452,13 @@
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;
// We don't set eTransactionNeeded, because just receiving a deferral
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 521659d..913f13a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -55,8 +55,6 @@
namespace android {
-// ---------------------------------------------------------------------------
-
class Client;
class Colorizer;
class DisplayDevice;
@@ -73,8 +71,6 @@
class SurfaceInterceptor;
}
-// ---------------------------------------------------------------------------
-
struct LayerCreationArgs {
LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
uint32_t flags, LayerMetadata);
@@ -106,14 +102,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,
@@ -281,17 +269,56 @@
ui::Transform::RotationFlags fixedTransformHint;
};
+ /*
+ * 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 +391,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;
@@ -403,23 +426,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 +442,194 @@
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; }
+
+ /*
+ * 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 +665,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 +672,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 +679,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 +693,13 @@
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;
+ void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
+ bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
- 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 +707,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 +733,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 +746,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 +766,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 +811,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 +821,40 @@
Rect getCroppedBufferSize(const Layer::State& s) const;
bool setFrameRate(FrameRate);
- virtual FrameRate getFrameRateForLayerTree() const;
- static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
+
+ // 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 +882,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,61 +953,9 @@
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;
@@ -1041,6 +1028,9 @@
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 +1049,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 +1075,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 +1096,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/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..f513535 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -98,11 +98,13 @@
}
DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
- uint32_t count, nsecs_t expectedVSyncTimestamp) {
+ uint32_t count, nsecs_t expectedVSyncTimestamp,
+ nsecs_t deadlineTimestamp) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
event.vsync.count = count;
event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
+ event.vsync.deadlineTimestamp = deadlineTimestamp;
return event;
}
@@ -200,9 +202,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(
@@ -283,12 +286,13 @@
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);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
- expectedVSyncTimestamp));
+ expectedVSyncTimestamp, deadlineTimestamp));
mCondition.notify_all();
}
@@ -410,9 +414,11 @@
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();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
- ++mVSyncState->count, expectedVSyncTime));
+ ++mVSyncState->count, expectedVSyncTime,
+ deadlineTimestamp));
}
}
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 49f624c..fa1ca64 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -57,7 +57,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 +66,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 +118,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;
@@ -157,7 +160,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,7 +181,8 @@
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);
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/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5f7b2c2..5271ccc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -39,7 +39,6 @@
#include <numeric>
#include "../Layer.h"
-#include "DispSync.h"
#include "DispSyncSource.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
@@ -50,6 +49,7 @@
#include "VSyncDispatchTimerQueue.h"
#include "VSyncPredictor.h"
#include "VSyncReactor.h"
+#include "VsyncController.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -95,6 +95,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 +124,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 +165,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 +180,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,20 +204,18 @@
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, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
+ auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
std::move(interceptCallback));
return createConnection(std::move(eventThread));
@@ -286,14 +308,15 @@
mConnections.at(handle).thread->dump(result);
}
-void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
RETURN_IF_INVALID_HANDLE(handle);
- mConnections[handle].thread->setPhaseOffset(phaseOffset);
+ mConnections[handle].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) {
@@ -318,19 +341,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 +363,6 @@
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
mSchedulerCallback.setVsyncEnabled(false);
- getPrimaryDispSync().endResync();
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
@@ -380,10 +402,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 +418,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 +431,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 +439,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 +573,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 +616,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..0b5c9d2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -40,11 +40,12 @@
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
@@ -68,10 +69,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,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -88,17 +89,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 +110,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*);
@@ -136,6 +135,7 @@
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 +151,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 +175,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 +187,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*,
@@ -280,6 +283,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/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 59c336a..c9c2d84 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -89,7 +89,7 @@
}
void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
- std::lock_guard<decltype(mMutex)> lk(mMutex);
+ std::lock_guard lock(mMutex);
using namespace std::literals;
static constexpr int ns_per_s =
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
@@ -109,7 +109,7 @@
}
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/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 2a2d7c5..0407900 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,54 @@
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;
+ };
+
+ /*
* 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 +152,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 ef92680..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();
}
@@ -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 2b642a2..819f3e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,7 +52,6 @@
#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 +74,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 +106,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 +116,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"
@@ -334,6 +333,7 @@
mInterceptor(mFactory.createSurfaceInterceptor(this)),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(std::make_unique<FrameTracer>()),
+ mFrameTimeline(std::make_shared<frametimeline::impl::FrameTimeline>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -509,6 +509,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() {
@@ -961,7 +970,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 +988,7 @@
return BAD_VALUE;
}
- mScheduler->getDisplayStatInfo(stats);
+ mScheduler->getDisplayStatInfo(stats, systemTime());
return NO_ERROR;
}
@@ -1037,7 +1046,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);
@@ -1452,7 +1461,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) {
@@ -1820,11 +1833,10 @@
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) {
@@ -1870,7 +1882,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",
@@ -2054,7 +2066,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);
}
@@ -2217,7 +2229,7 @@
ALOGV("postComposition");
nsecs_t dequeueReadyTime = systemTime();
- for (auto& layer : mLayersWithQueuedFrames) {
+ for (auto layer : mLayersWithQueuedFrames) {
layer->releasePendingBuffer(dequeueReadyTime);
}
@@ -2243,7 +2255,7 @@
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,
@@ -2681,6 +2693,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 +2925,9 @@
setInputWindowsFinished();
}
+ for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
+ mInputFlinger->setFocusedWindow(focusRequest);
+ }
mInputWindowCommands.clear();
}
@@ -2929,10 +2945,6 @@
mInputFlinger->setInputWindows(inputInfos,
mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
: nullptr);
- for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
- mInputFlinger->setFocusedWindow(focusRequest);
- }
- mInputWindowCommands.focusRequests.clear();
}
void SurfaceFlinger::commitInputWindowCommands() {
@@ -2984,14 +2996,16 @@
// start the EventThread
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
+ const auto configs = mVsyncConfiguration->getCurrentConfigs();
mAppConnectionHandle =
mScheduler->createConnection("app",
- mVsyncConfiguration->getCurrentConfigs().late.appOffset,
+ /*workDuration=*/configs.late.appWorkDuration,
+ /*readyDuration=*/configs.late.sfWorkDuration,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
mScheduler->createConnection("sf",
- mVsyncConfiguration->getCurrentConfigs().late.sfOffset,
- [this](nsecs_t timestamp) {
+ /*workDuration=*/configs.late.sfWorkDuration,
+ /*readyDuration=*/0ns, [this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
@@ -3024,8 +3038,12 @@
}
void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, config.appOffset);
- mScheduler->setPhaseOffset(mSfConnectionHandle, config.sfOffset);
+ mScheduler->setDuration(mAppConnectionHandle,
+ /*workDuration=*/config.appWorkDuration,
+ /*readyDuration=*/config.sfWorkDuration);
+ mScheduler->setDuration(mSfConnectionHandle,
+ /*workDuration=*/config.sfWorkDuration,
+ /*readyDuration=*/0ns);
}
void SurfaceFlinger::commitTransaction() {
@@ -3129,7 +3147,7 @@
if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
- mLayersWithQueuedFrames.push_back(layer);
+ mLayersWithQueuedFrames.emplace(layer);
} else {
ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
@@ -3470,7 +3488,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);
@@ -3938,7 +3960,7 @@
}
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
- const bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
+ bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands);
return hasChanges ? eTraversalNeeded : 0;
}
@@ -4342,8 +4364,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)},
@@ -4490,7 +4511,7 @@
}
mScheduler->dump(mAppConnectionHandle, result);
- mScheduler->getPrimaryDispSync().dump(result);
+ mScheduler->dumpVsync(result);
}
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4717,8 +4738,6 @@
result.append("Build configuration:");
colorizer.reset(result);
appendSfConfigString(result);
- appendUiConfigString(result);
- appendGuiConfigString(result);
result.append("\n");
result.append("\nDisplay identification data:\n");
@@ -4816,9 +4835,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";
@@ -5025,9 +5045,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1036 are currently used for backdoors. The code
+ // Numbers from 1000 to 1037 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 <= 1037) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5170,14 +5190,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
@@ -5369,6 +5389,23 @@
}
return NO_ERROR;
}
+ // Inject a hotplug connected event for the primary display. This will deallocate and
+ // reallocate the display state including framebuffers.
+ case 1037: {
+ const auto token = getInternalDisplayToken();
+
+ sp<DisplayDevice> display;
+ {
+ Mutex::Autolock lock(mStateLock);
+ display = getDisplayDeviceLocked(token);
+ }
+ const auto hwcId =
+ getHwComposer().fromPhysicalDisplayId(PhysicalDisplayId(*display->getId()));
+
+ onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
+
+ return NO_ERROR;
+ }
}
}
return err;
@@ -5484,7 +5521,7 @@
}
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
status_t validate = validateScreenshotPermissions(args);
@@ -5510,8 +5547,16 @@
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.
+ if (args.useRGBColorSpace) {
+ dataspace = Dataspace::V0_SRGB;
+ } else {
+ const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+ dataspace = pickDataspaceFromColorMode(colorMode);
+ }
}
RenderAreaFuture renderAreaFuture = promise::defer([=] {
@@ -5522,8 +5567,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) {
@@ -5566,7 +5612,7 @@
}
status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
- ScreenCaptureResults& captureResults) {
+ const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<DisplayDevice> displayWeak;
ui::Size size;
@@ -5587,8 +5633,7 @@
}
RenderAreaFuture renderAreaFuture = promise::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size,
- captureResults.capturedDataspace,
+ return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
false /* useIdentityTransform */,
false /* captureSecureLayers */);
});
@@ -5598,11 +5643,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);
@@ -5668,8 +5713,16 @@
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.
+ if (args.useRGBColorSpace) {
+ dataspace = Dataspace::V0_SRGB;
+ } else {
+ const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
+ dataspace = pickDataspaceFromColorMode(colorMode);
+ }
captureSecureLayers = args.captureSecureLayers && display->isSecure();
} // mStateLock
@@ -5689,7 +5742,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;
@@ -5712,13 +5765,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.
@@ -5728,54 +5781,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,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6497f6b..28762c9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -98,6 +98,10 @@
class TimeStats;
class FrameTracer;
+namespace frametimeline {
+class FrameTimeline;
+}
+
namespace os {
class IInputFlinger;
}
@@ -183,8 +187,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 +223,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 +274,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 +282,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&&);
@@ -334,6 +338,24 @@
bool mDisableClientCompositionCache = false;
void setInputWindowsFinished();
+protected:
+ // We're reference counted, never destroy SurfaceFlinger directly
+ virtual ~SurfaceFlinger();
+
+ 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:
friend class BufferLayer;
friend class BufferQueueLayer;
@@ -349,21 +371,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,17 +414,99 @@
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(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;
+ };
+
+ 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...)) {
+ setVsyncConfig(*config);
+ }
+ }
+
+ static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+
+ // 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;
@@ -427,11 +528,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 +596,14 @@
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
- /* ------------------------------------------------------------------------
- * DeathRecipient interface
- */
+
+ // 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 +616,7 @@
const hal::VsyncPeriodChangeTimeline& updatedTimeline) override;
void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override;
- /* ------------------------------------------------------------------------
+ /*
* ISchedulerCallback
*/
@@ -540,7 +638,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 +647,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.
@@ -603,9 +692,6 @@
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&);
@@ -616,11 +702,9 @@
*/
bool handlePageFlip();
- /* ------------------------------------------------------------------------
+ /*
* Transactions
*/
- using TransactionSchedule = scheduler::TransactionSchedule;
-
void applyTransactionState(const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
@@ -652,24 +736,7 @@
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,
@@ -715,23 +782,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 +800,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 +841,7 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
- /* ------------------------------------------------------------------------
+ /*
* H/W composer
*/
@@ -814,7 +867,7 @@
// acquiring mStateLock.
HWComposer& getHwComposer() const;
- /* ------------------------------------------------------------------------
+ /*
* Compositing
*/
void invalidateHwcGeometry();
@@ -828,7 +881,7 @@
void postFrame();
- /* ------------------------------------------------------------------------
+ /*
* Display management
*/
sp<DisplayDevice> setupNewDisplayDeviceInternal(
@@ -848,7 +901,7 @@
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
- /* ------------------------------------------------------------------------
+ /*
* VSYNC
*/
nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
@@ -912,33 +965,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;
@@ -982,7 +1008,7 @@
void onFrameRateFlexibilityTokenReleased();
- /* ------------------------------------------------------------------------
+ /*
* VrFlinger
*/
void resetDisplayState() REQUIRES(mStateLock);
@@ -992,10 +1018,26 @@
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 +1084,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 +1103,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
@@ -1095,6 +1132,7 @@
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
+ const std::shared_ptr<frametimeline::FrameTimeline> mFrameTimeline;
bool mUseHwcVirtualDisplays = false;
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
@@ -1130,40 +1168,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 +1179,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
@@ -1206,7 +1209,7 @@
SurfaceFlingerBE mBE;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
- /* ------------------------------------------------------------------------
+ /*
* Scheduler
*/
std::unique_ptr<Scheduler> mScheduler;
@@ -1225,29 +1228,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..fb08e69 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 {
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index a0dd999..753476e 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
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/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/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..3261308 100644
--- a/services/surfaceflinger/tests/DetachChildren_test.cpp
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -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->getHandle(),
+ 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);
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/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/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index e3b9489..ab74c50 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -167,7 +167,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 +176,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/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 690f758..962a0cf 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,7 +103,7 @@
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);
@@ -307,21 +306,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 +324,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 +477,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 +499,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 +543,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.
@@ -604,8 +595,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.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 8d97f27..8570032 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -422,7 +422,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 +819,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/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 8d4aa0c..6f1f1f2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -41,6 +41,7 @@
"DisplayIdentificationTest.cpp",
"DisplayTransactionTest.cpp",
"EventThreadTest.cpp",
+ "FrameTimelineTest.cpp",
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
@@ -68,19 +69,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",
@@ -89,7 +92,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..159a215 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -41,10 +41,10 @@
#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 +142,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());
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/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 7fade0d..049264e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -24,6 +24,7 @@
#include <type_traits>
#include <android/hardware/power/Boost.h>
+#include <binder/IPCThreadState.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/impl/Display.h>
@@ -37,6 +38,7 @@
#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>
@@ -45,12 +47,12 @@
#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/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -155,7 +157,8 @@
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
- mock::DispSync* mPrimaryDispSync = new mock::DispSync;
+ 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;
@@ -213,7 +216,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);
}
@@ -1228,8 +1232,12 @@
// --------------------------------------------------------------------
// 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
@@ -1325,8 +1333,6 @@
// The call disable vsyncs
EXPECT_CALL(mSchedulerCallback, setVsyncEnabled(false)).Times(1);
- // The call ends any display resyncs
- EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1);
// --------------------------------------------------------------------
// Invocation
@@ -3130,7 +3136,7 @@
// processing.
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
// --------------------------------------------------------------------
// Invocation
@@ -3261,20 +3267,14 @@
};
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);
+ 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 setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-
- static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {}
+ static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
};
// --------------------------------------------------------------------
@@ -3297,7 +3297,7 @@
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -3326,7 +3326,6 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::DispSync::setupEndResyncCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -3361,7 +3360,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -3379,7 +3378,7 @@
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupBeginResyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
@@ -3389,7 +3388,6 @@
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);
}
};
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index aab6d01..ae94f16 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,
@@ -159,10 +163,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 +235,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 +264,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 +305,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 +320,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 +342,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 +372,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 +392,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 +426,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 +446,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 +461,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..85d2834
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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->kMaxDisplayFrames;
+ 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;
+ }
+
+ std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
+ impl::TokenManager* mTokenManager;
+ FenceToFenceTimeMap fenceFactory;
+ uint64_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);
+}
+
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index f6bf05a..409f90d 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();
@@ -139,17 +135,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 +278,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..35619a3 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));
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6c01f85..d4591fc 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 {
@@ -175,14 +175,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/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ebb2d76..db3e0bd 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,12 +19,13 @@
#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 +33,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}) {}
@@ -95,7 +98,7 @@
// 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..2d12451 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -189,7 +189,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 +218,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));
@@ -361,6 +362,10 @@
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.
*/
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 44b3dc0..28415bc 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;
@@ -123,7 +125,7 @@
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);
@@ -156,7 +158,7 @@
// 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,
@@ -179,7 +181,7 @@
// 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;
@@ -244,7 +246,7 @@
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
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 f630e3b..69731fd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -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();
}
@@ -228,7 +230,11 @@
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);
}
}
@@ -237,7 +243,11 @@
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));
@@ -249,7 +259,7 @@
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));
@@ -265,7 +275,11 @@
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) {
@@ -273,7 +287,11 @@
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);
}
@@ -282,7 +300,11 @@
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);
}
@@ -292,7 +314,11 @@
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());
@@ -309,7 +335,11 @@
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());
@@ -339,8 +369,8 @@
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();
@@ -367,8 +397,9 @@
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);
}
@@ -380,9 +411,9 @@
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();
}
@@ -395,9 +426,9 @@
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();
}
@@ -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));
@@ -446,8 +479,8 @@
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);
}
@@ -460,18 +493,18 @@
.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));
}
@@ -482,10 +515,16 @@
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();
}
@@ -493,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));
@@ -519,16 +568,16 @@
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) {
@@ -541,12 +590,12 @@
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();
@@ -558,8 +607,8 @@
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) {
@@ -569,31 +618,43 @@
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, 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));
}
@@ -604,16 +665,24 @@
.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) {
@@ -621,18 +690,26 @@
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, 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();
}
@@ -642,12 +719,12 @@
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();
}
@@ -656,14 +733,14 @@
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();
}
@@ -675,12 +752,16 @@
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));
@@ -696,12 +777,16 @@
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));
@@ -715,8 +800,12 @@
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);
@@ -737,8 +826,12 @@
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);
@@ -766,16 +859,44 @@
.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 {
@@ -788,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());
@@ -796,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));
@@ -816,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));
@@ -829,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));
@@ -861,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));
@@ -880,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();
@@ -891,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))
@@ -921,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/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/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 054aaf8..eefdec1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -35,7 +35,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> &));
diff --git a/libs/gui/GuiConfig.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
similarity index 63%
rename from libs/gui/GuiConfig.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index 3ec20ee..8a18123 100644
--- a/libs/gui/GuiConfig.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.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,16 @@
* limitations under the License.
*/
-#include <gui/GuiConfig.h>
+#include "mock/MockVSyncTracker.h"
+#include <thread>
+using namespace std::chrono_literals;
namespace android {
+namespace 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.
+VSyncTracker::VSyncTracker() = default;
+VSyncTracker::~VSyncTracker() = default;
-}; // namespace android
+} // namespace mock
+} // namespace android
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%
copy from libs/gui/GuiConfig.cpp
copy 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..1d87546
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -0,0 +1,40 @@
+/*
+ * 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 {
+namespace 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 mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 081d18b..ca3551e 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.useRGBColorSpace = 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;
+ }
+
+ 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.useRGBColorSpace = true;
+ 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/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/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/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/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());
}